If you want to do something to a list of list of X
Is it idiomatic to just have 2 nested maps?
I think so. Nested map
s, or, for/list
s. Whichever you prefer. If map
, you could use curry
to make it even more terse if you prefer. #lang racket/base
(require racket/function)
(define xss '((1 2) (3 4) (5 6)))
(map (curry map add1) xss)
(for/list ([outer (in-list xss)])
(for/list ([inner (in-list outer)])
(add1 inner)))
Aside from https://docs.racket-lang.org/style/ I’m not sure Racket has a strong sense of “idiomatic”, compared to some other languages.
Maybe just common sense things like “local consistency”. e.g. In a project or a certain file, maybe pick one and generally stick with it.
Where “it” is some choice like define
vs. let
, map
and friends vs. list comprehensions, and so on.
I think most Racketeers will be OK with the choice. They’ll just appreciate reducing cognitive load locally. I mean, at least that would be what I’d say. ¯_(ツ)_/¯
To clarify I mean style for #lang racket
or #lang racket/base
. Setting aside the tiny matter of what’s idiomatic for #lang looks-nothing-like lang-racket
:slightly_smiling_face:
is OK with someone’s style provided they pass a Voight-Kampff test.
Ah thanks!
@timmstelzer has joined the channel
Hey there! I’ve got a question regarding recursion; Just started doing euler problems to get into racket and was wondering if there is a better / “idiomatic” way to write this: https://github.com/tstelzer/problems/blob/master/racket/project-euler/problem-2.rkt#L11-L16?
Specifically, I was wondering how I can avoid that last expression ((loop)
), it feels a bit redundant
@timmstelzer you probably want the let loop
form
(let loop ([xs '()] [a 0] [b 1])
(if (> (+ a b) ceil)
xs
(loop (stream-cons (+ a b) xs) b (+ a b)))))
it’s also worth pointing out that you don’t get any laziness from streams with that loop
oh?
but when i run (stream-fib <some ceiling>)
i get back a stream
doesn’t that imply that its lazy?
no. the way you’re building up the list with an iterative loop means the entire stream is built before any value is produced.
ah hm
you technically avoid evaluation of the (+ a b)
expression inside the stream-cons
form until the stream is realized, but that’s it.
if you want a lazy version, you should make the loop non tail-recursive.
(i admit, i wasn’t aware that my loop was tail-recursive)
@samth nice, works!
the Racket streams API is unfortunately rather inexpressive. the traditional way to formulate the fibonacci sequence with streams is something like: (define fibs (stream-cons 0 (stream-cons 1 (stream-map + fibs (stream-rest fibs)))))
…but Racket’s stream-map
only allows one stream argument.
it also sadly doesn’t provide a stream-take-while
function, and the one from srfi/41
doesn’t work with streams created with racket/stream
.
hm, i see — this was a shot-in-the-dark anyways, haven’t worked with streams yet :slightly_smiling_face:
well, i can fortunately just remove all the stream-
prefixes from my code and it will still work :smile:
though i do wonder about lazyness
yeah. and in any case, if you want a version that still uses named let
that’s actually lazy, you can do this: (define (stream-fibs ceil)
(let loop ([a 0]
[b 1])
(let ([c (+ a b)])
(if (> c ceil)
empty-stream
(stream-cons c (loop b c))))))
but I think this misses out on why streams are nice, since a stream-take-while
function would make it possible to define an infinite stream of fibonacci numbers without worrying about needing to take up to a certain condition.
hm true
thanks for the explanations, very much appreciate it!
Do Racket people write their own take-while function then?
I don’t think people actually use racket/stream
oh
What is a more standard solution?
or just srfi 41?
I don’t think Racket programmers often use lazy streams