Hi all, I am trying to get my head around combining functions to create a new function, my question is, is there an easier way than the following to create a new function from two others?
#lang racket
(define (plus-two)
(λ (x) (+ x 2)))
(define (plus-three)
(λ (x) (+ x 3)))
(define (combine l1 l2)
(λ (x) ((l2) ((l1) x))))
((combine plus-two plus-three) 2)
The final line uses the combine function to correctly give the result 7
@mark.warren You don’t have to make plus-two
and plus-three
return functions, they can just be functions: (define (plus-two x)
(+ x 2))
(define (plus-three x)
(+ x 3))
(define ((combine l1 l2) x)
(l2 (l1 x)))
((combine plus-two plus-three) 2) ;; results in 7
@notjack Ah thanks, that’s probably easier.
I proposed a simple programming assignment for fun in another slack which was to implement zip
I initially came up with: (define (zipn . args)
(let loop ([ lists args ][ result '() ])
(cond [ (ormap empty? lists) (reverse result) ]
[ else (loop (map rest lists) (cons (map first lists) result)) ])))
Then someone posted a Clojure example: (defn zipn [& lists] (apply map vector lists))
which showed me I was missing a fundamental understanding :slightly_smiling_face: After a little digging, I came up with: (define (zipn . args)
(apply map list args))
However it will fail if the lists are not all of equal length. Also, I’m not exactly sure how apply
is being used here. I’ve always used it as (apply procedure list)
, so I was surprised to see both map
and list
there.
Regarding the apply
: the expression (map list '((a b) (1 2))
doesn’t work, but (map list '(a b) '(1 2)
does. So (apply map list '((a b) (1 2)))
turns into (map list '(a b) '(1 2))
.
(and (map list '(a b) '(1 2))
turns into (list (list 'a '1) (list 'b '2))
)
Ok, so I guess I need a special version of map
to not blow up when the lists are of unequal length.
Thanks @soegaard2 - I have a better intuitive grasp now. I was overcomplicating apply
in my head.
The first argument to apply
(in this case map
) is the function to be applied, and the last argument (in this case args
) is a list of arguments to pass to it. If there are more arguments in the middle (in this case just one, list
), then those are individual arguments that will be added to the beginning of the argument list (args
) before it’s passed in.
In short, (apply map list args)
is equivalent to (apply map (cons list args))
.
I think I prefer Clojure’s map
which stops when any of the argument lists is exhausted.
Common Lisp map
does that too
using map
for both single-sequence transforms and multiple-sequence zip-style transforms is confusing, IMO
I dunno if this is important in Racket these days, but at some point I started to avoid using apply
for this, because I was using a language (either Racket or JS) where it seemed likr the argument pointers were put on the stack individually, resulting in a stack overflow if the list was long enough. As a result of that, I have a better feeling about your original zipn
, which doesn’t apply
a dynamically sized list.
I hope it wasn’t Racket @rokitna !
My memory about this is very hazy :) don’t mean to scaremonger
Two questions: 1) Does Racket have a map
of another name that ends when the shortest arg list ends?, and if not, 2) What would be a good name for such a function?
I think making a (zip f lst ...)
function with an optional #:allow-uneven-lists?
keyword argument would be a decent approach
Interesting. Then the traditional zip
would be (curry zip list)
(I bet it was JS. I remember stack size limits first really annoyed me when I moved to JS and kept using a tail-calling style when JS didn’t have TCE.)
I wouldn’t be worried about the stack in Racket.
However - in Scheme apply
is required to make allocate a new list (of the arguments). I think Racket does the same: (let ()
(define xs (list '(a b c)))
(eq? (apply values xs) xs))
That is, using apply
to implement zip
is a non-optimal, since it allocates an unnecessary temporary list.
Tangent: I don’t think there is such a requirement in RNRS, it just may be easier to implement that way. I seem to recall in the Dybvig paper about implementing call-with-values
he talks about optimizing varargs to use primitives to directly access the stack/call frame instead of building a list (eg. when the varargs are actually a kind of optional variable from the caller.)
Hmm. I think you are right.
R5RS says about apply
: procedure: (apply proc arg1 ... args)
Proc must be a procedure and args must be a list. Calls proc with the elements of the list (append (list arg1 ...) args) as the actual arguments.
And it says this about append
: The resulting list is always newly allocated, except that it shares structure with the last list argument.
Does that mean that (eq? (apply values xs) xs))
is #t in a Scheme?
My guess is that it is undefined behavior.
:stuck_out_tongue:
(apply values xs)
would return multiple values so is probably a bad example?
Maybe (apply (lambda ys ys) xs)
is more apropo.