isPos (r,c) = elem r [0..4] && elem c [0..r]
I first wrote this:
(define (is-pos r c) (and (member r (lgen 0 4))
(member c (lgen 0 r))))
where lgen is:
(define (lgen m n) (build-list (+ 1 (- n m)) (λ (x) (+ x m))))
I think (lgen 0 r) is a reasonable alternative to [0..r], and the minor additional length of the Racket version is fine.
I then decided that I may prefer to a list of row/col instead of individual args and bumped into a need for destructuring a list, so I wrote this macro:
(define-syntax defpat
(syntax-rules ()
[(_ (fn pat) b1 b2 ...)
(define fn (match-lambda [pat b1 b2 ...]))]))
which allows:
(defpat (is-pos (list r c)) (and (member r (lgen 0 4))
(member c (lgen 0 r))))
The fact that this is such a common operation and I couldn't find anything built-in makes me think that I may be missing something. Is this a reasonable solution? Are there better alternatives?
I suppose a better name might be in order since it's not matching one of several patterns; in this case, it's really just for destructuring a list more concisely.
I'm still blown away by how easy it was to mold Racket closer to what I wanted. I've just barely begun learning macros, but syntax-rules made this pretty easy.
I think the only thing that sticks out is the "list" function, but that seems like a reasonable sacrifice given the flexibility it allows for in more complicated patterns.
Thanks,
Brian
--
Brian Adkins
Lojic Technologies, LLC
http://lojic.com/
____________________
Racket Users list:
http://lists.racket-lang.org/users
> I'm porting more Haskell code to Racket as a learning exercise. When I got to this line:
>
> isPos (r,c) = elem r [0..4] && elem c [0..r]
>
> I first wrote this:
>
> (define (is-pos r c) (and (member r (lgen 0 4))
> (member c (lgen 0 r))))
>
> where lgen is:
>
> (define (lgen m n) (build-list (+ 1 (- n m)) (λ (x) (+ x m))))
>
Have you considered turning your Haskell code into prefix and leaving it otherwise alone (mostly):
#lang racket
;; -----------------------------------------------------------------------------
;; prelude
(module+ test (require rackunit))
(define-syntax-rule
(= (f x ...) expression)
(define (f x ...) expression))
(define (.. low high) (range low high))
(define (elem r rng) (cons? (member r rng)))
;; -----------------------------------------------------------------------------
;; Haskell to Racket
;; isPos (r,c) = elem r [0..4] && elem c [0..r]
((isPos r c) . = . (and (elem r [.. 0 4]) (elem c [.. 0 r])))
;; -----------------------------------------------------------------------------
;; some tests
(module+ test
(check-true (isPos 1 0))
(check-true (isPos 2 1))
(check-false (isPos 1 2))
(check-false (isPos 5 1)))
Okay, the line is still a bit wider but that's because of extra spaces -- Matthias
I think you want define/match.
(define/match (is-pos l)
[((list r c)) (and (member r (range 0 4))
(member c (range 0 r)))])
(is-pos '(2 5)) ; => #f
(is-pos '(2 1)) ; => '(1)
/Jens Axel
--
--
Jens Axel Søgaard
Consider reworking the use of the "and" form and everything within it:
instead, test "r" and "c" for "integer?", and then use "<=" and ">=" to
determine whether in range.
Otherwise, you're constructing lists of numbers on each call to
"is-pos". This might be practically OK in this particular case, since
the lists will always be small, but it's not something you'd want to do
if the lists could ever be huge. And I'd say it's not really idiomatic
Racket for purposes of your learning exercise.
Remember that Racket is an imperative language; Racket is not Haskell,
nor is Racket a mathematician. :)
Neil V.
Brian Adkins wrote at 07/12/2014 12:53 PM:
> I'm porting more Haskell code to Racket as a learning exercise. When I got to this line:
>
> isPos (r,c) = elem r [0..4] && elem c [0..r]
>
> I first wrote this:
>
> (define (is-pos r c) (and (member r (lgen 0 4))
> (member c (lgen 0 r))))
>
> where lgen is:
>
> (define (lgen m n) (build-list (+ 1 (- n m)) (λ (x) (+ x m))))
[...]
(defpat (is-pos (list r c))
(and (member r (lgen 0 4))
(member c (lgen 0 r))))
seems nicer to me than:
(define/match (is-pos l)
[((list r c)) (and (member r (lgen 0 4))
(member c (lgen 0 r)))])
for two reasons:
1) It's odd to me to specify the l argument, and then never refer to it.
2) The syntax of the former seems less "noisy".
I can see the advantage of define/match when you have more than one pattern, and I think there's a disadvantage in introducing too many macros that are close to existing, standard, ones, but I *think* this is going to be a common enough situation for me that being slightly more concise and not creeping to the right may be worth it. I'll hold the opinion loosely though until I have a bunch more code reading/writing under my belt.
Brian
The Haskell code was written a few years ago, as a relative newbie, as an exercise in emphasizing readability and conciseness, as well as showing how it can be close to mathematical notation, hence:
elem r [0..4] && elem c [0..r]
vs.
r >= 0 && r <= 4 && c >= 0 && c <= r
In other words, I wanted to express that r should be an element of the set [0..4], etc. w/o worrying about performance.
It does seem to be a tendency in Haskell code - for example, the quicksort implementation is beautiful, but doesn't use one of the key ideas of swapping in place. It makes for a great demo, but wouldn't be something you'd want in a performance-sensitive section of code.
I am interested in learning the idioms of the Racket community (especially before contributing code), but for this exercise, I wanted to see how close I could get to the original. A separate exercise would be to solve the problem from scratch in a more idiomatic style. It's a toy program (solves the Cracker Barrel pegboard puzzle):
http://blog.lojic.com/2011/03/10/cracker-barrel-peg-board-puzzle-in-haskell/
I didn't want to spend the time on a fresh look, so a transliteration was what I was shooting for. I'm not going to try and code Haskell in Racket, or convert Racket to Raskell :) But, I do like the fact that Racket seems flexible enough to allow me to steal some good ideas from other languages and incorporate them into my Racket code.
Brian
r >= 0 && r <= 4 && c >= 0 && c <= r
0 <= c <= r <= 4
(define (is-pos r c)(<= 0 c r 4))
Thanks for the kind words. :)
This was an experiment that tried to address a few questions that
repeatedly get asked on the mailing list:
1) that you can't destructure data structures at the binding site, and
2) that you need to manually define many different versions of each
binding construct (eg define, match-define, match-define-values, etc),
which inevitably means that some binding forms don't exist (eg
match-for/X forms)
Something like the expressivity problem for binding forms.
I tried to implement one "generic" version of each binding form that
would accept many different "binding instances", so every binding form
would be extensible by implementing the appropriate instance (eg, a
matching instance, a values instance)
Every Racket binding form should have a ~-prefixed version in my
library and my version should work as a drop-in replacement for its
Racket counterpart if you want to try it out.
Here's a link to the docs if you're interested in reading more:
http://stchang.github.io/generic-bind/generic-bind.html
(You can tell that it hasn't been updated in a while from the old css.)
Caveat: my macro-fu hasn't quite reached expert level so I'd love for
anyone to tell me where my code is bad.
That being said, I ran a large part of the Racket test suite using my
binding forms instead of the Racket ones so they should be usable in
most scenarios.
The idea was to support a generic interface so different instances
could be defined, but thus each instance needs it's own name. For
example, I need to differentiate when I'm binding a match pattern vs
binding multiple values.
I tried to define shortcuts for some match patterns like $list and I
suppose you could rename them to be the same as racket but I didnt
want to clash with racket's names. There's also define-match-bind to
create your own shortcuts.
> And is there anything like a “generic-binding-expander” that would act sort of like a match-expander?
No there isnt. It was a todo but I never got around to it. Did you
have something specific in mind?
> And is there some kind of "generic-bind/renamed” module or something that provides everything from generic-bind except without the ~ in front?
Good idea. Yes, in hindsight my choice of names was probably
suboptimal. I've incorporated your suggestion so now you can do
(require generic-bind/as-rkt-names), which drops all ~-prefixes
(except for ~vs). Thanks!
There's a define-match-bind that consumes a match pattern and defines
a $-prefixed name for that specific pattern that can be used in
gen-bind positions. Does that help?