(define (fork f g)
(lambda x
(apply f (map g x))))
which comes in handy, for example, when sorting an alist, because
it allows you to write
(sort (fork < car) alist)
instead of
(sort (lambda (x y) (< (car x) (car y))) alist)
Of course the idea is so obvious that it probably has been invented
or discovered dozens of times by now. Hence I wonder if there is a
common name for this kind of function. I seem to remember that APL
used a similar operator, but its name escapes me right now.
Any ideas?
--
Nils M Holm | http://t3x.org
In Common Lisp one usually does that directly in the SORT call itself
using the :KEY keyword:
(sort alist #'< :key #'car)
In the case of SORT, the predicate is a required parameter, so one
doesn't write ":TEST #'<", but most of the other CL sequence functions
[functions that work on both lists and vectores] support a :TEST keyword
to provide a predicate other than the default #'EQL, as well as a number
of others -- :START :END :KEY :TEST :TEST-NOT :FROM-END [and usually
:START2 & :END2 as well, if the function works on two sequences].
http://www.lispworks.com/documentation/HyperSpec/Body/f_find_.htm
http://www.lispworks.com/documentation/HyperSpec/Body/f_pos_p.htm
http://www.lispworks.com/documentation/HyperSpec/Body/f_search.htm
http://www.lispworks.com/documentation/HyperSpec/Body/f_mismat.htm
[Note: This is *not* intended to start any sort of "CL vs. Scheme"
flamefest, only to provide an example of a different style of
accomplishing the OP's desired functionality. Anyway, some Schemes
provide keywords in LAMBDAs, don't they?]
-Rob
-----
Rob Warnock <rp...@rpw3.org>
627 26th Avenue <URL:http://rpw3.org/>
San Mateo, CA 94403 (650)572-2607
I always found those Common Lisp keywords a bit confusing, but I
begin to see that there is some orthogonality here. It is just a
different approach with its own beauty. (I am currently working
my way through CLTL2 and PAIP, just to get a glimpse at the Common
Lisp way of problem-solving.)
> [Note: This is *not* intended to start any sort of "CL vs. Scheme"
> flamefest, only to provide an example of a different style of
> accomplishing the OP's desired functionality. Anyway, some Schemes
> provide keywords in LAMBDAs, don't they?]
Ah, now that Scheme is officially dead and people start renaming their
Implementations to avoid the association of decay, why not discuss some
Common Lisp in this group?
Yes, some Scheme implementations provide keywords and people use those
keywords and thereby make their programs utterly non-portable. But why
not? Portable Scheme is about as practical as portable C, so everyone
creates their own pet language and enjoys it. If there is such a thing
as the Scheme way then this is it.
Are you talking about the "axis" operator of APL? "f[i] array" would
apply f along the ith axis (or dimension) of a multidimensional array.
Though your `fork' is not quite the same.
No, what I meant is actually called a "fork" in APL. It does not appear
to be an operator, but works in a similar way as my version of fork:
x (f g h) y --> (x f y) g (x h y)
Or, in Scheme:
(define (apl-fork x f g h y) (g (f x y) (h x y)))
Indeed. I first saw hook and fork in J. I should've known they were
first proposed for APL (see http://www.jsoftware.com/papers/fork.htm)
> Or, in Scheme:
>
> (define (apl-fork x f g h y) (g (f x y) (h x y)))
I'd rewrite this as
(define (apl-fork f g h) (lambda (x y) (g (f x y) (h x y))))
But this doesn't quite work for sorting an alist. You want
(sort (lambda (x y) (< (car x) (car y))) alist)
while in a fork both f and h take the exact same argument(s).
Incidentally, the infix form looks odd in Scheme! We may as well
use Curry's phi combinator (fork's inspiration):
(define (phi f g h) ; or ϕ if your Scheme groks unicode!
(lambda args (f (apply g args) (apply h args))))
((phi * + -) 7 5) => 24
((phi + sin^2 cos^x) 42) => 1.
I would love to see some useful examples of hook and fork. In the
sort of programming I do, I have not found any use for them.
Hi Nils,
Here's a similar example done in Factor.
! Start Factor:
~/Downloads/factor $ rlwrap ./factor -run=listener
! Put an array of two element arrays on the stack:
( scratchpad ) { { 1 2 } { 3 4 } { 5 6 } { 7 8 } }
--- Data stack:
{ ~array~ ~array~ ~array~ ~array~ }
! Put a quotation on the stack and sort using it:
( scratchpad ) [ [ first ] bi@ >=< ] sort
--- Data stack:
{ ~array~ ~array~ ~array~ ~array~ }
! Print the result:
( scratchpad ) .
{ { 7 8 } { 5 6 } { 3 4 } { 1 2 } }
( scratchpad )
So, the quotation:
[ [ first ] bi@ >=< ]
is doing something similar to the usage of fork in your example.
The docs for 'bi@' are at:
http://docs.factorcode.org/content/article-apply-combinators.html
Let's break down a use of 'bi@'.
! Put a couple of two element arrays on the stack:
( scratchpad ) { 1 2 } { 3 4 }
--- Data stack:
{ 1 2 }
{ 3 4 }
! Put a quotation on the stack
( scratchpad ) [ first ]
--- Data stack:
{ 1 2 }
{ 3 4 }
[ first ]
! Call 'bi@'
( scratchpad ) bi@
--- Data stack:
1
3
( scratchpad )
I've experimented with using Factor's dataflow combinators in Scheme.
There's a few in this library (not all are exported):
http://github.com/dharmatech/dharmalab/blob/master/combinators/concatenative.sls
You can see that 'bi@' is defined as:
(define (bi@ f c)
(lambda (x y)
(c (f x)
(f y))))
Ed
(define (phi f g h . args)(f (apply g args)(apply h args)))
1:=> (phi list min max 4 4 88 9 3 77 55)
(3 88)
You're doing a map and an apply, so why not call it mapply?
1:=> (define (mapply g f)(lambda args (apply g (map f args))))
mapply
1:=> (sort (mapply < car) '((55 :foo)(22 :bar)(33 :who)))
((22 :bar) (33 :who) (55 :foo))