Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

fork operator

2 views
Skip to first unread message

Nils M Holm

unread,
Jul 11, 2010, 3:45:45 AM7/11/10
to

I am sometimes using the following function

(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

Rob Warnock

unread,
Jul 11, 2010, 10:42:24 PM7/11/10
to
Nils M Holm <news...@t3x.org> wrote:
+---------------
+---------------

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

Nils M Holm

unread,
Jul 12, 2010, 5:48:29 AM7/12/10
to
Rob Warnock <rp...@rpw3.org> wrote:
> Nils M Holm <news...@t3x.org> wrote:
> +---------------
> | (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)
> +---------------
>
> 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].

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.

Bakul Shah

unread,
Jul 12, 2010, 2:36:19 PM7/12/10
to

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.


Nils M Holm

unread,
Jul 13, 2010, 1:22:59 AM7/13/10
to
Bakul Shah <use...@bitblocks.com> wrote:
> On 7/11/10 12:45 AM, Nils M Holm wrote:
> > (define (fork f g)
> > (lambda x
> > (apply f (map g x))))
>
> 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)))

Bakul Shah

unread,
Jul 13, 2010, 4:06:03 AM7/13/10
to
On 7/12/10 10:22 PM, Nils M Holm wrote:
> Bakul Shah<use...@bitblocks.com> wrote:
>> On 7/11/10 12:45 AM, Nils M Holm wrote:
>>> (define (fork f g)
>>> (lambda x
>>> (apply f (map g x))))
>>
>> 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)

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.

Eduardo Cavazos

unread,
Jul 13, 2010, 4:29:59 AM7/13/10
to

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

w_a_x_man

unread,
Jul 13, 2010, 12:08:54 PM7/13/10
to
On Jul 13, 3:06 am, Bakul Shah <use...@bitblocks.com> wrote:
> On 7/12/10 10:22 PM, Nils M Holm wrote:
>
> > Bakul Shah<use...@bitblocks.com>  wrote:
> >> On 7/11/10 12:45 AM, Nils M Holm wrote:
> >>>           (define (fork f g)
> >>>             (lambda x
> >>>               (apply f (map g x))))
>
> >> 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)
>
> Indeed. I first saw hook and fork in J. I should've known they were
> first proposed for APL (seehttp://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.

(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)

w_a_x_man

unread,
Jul 13, 2010, 12:54:46 PM7/13/10
to
On Jul 11, 2:45 am, Nils M Holm <news2...@t3x.org> wrote:

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))

0 new messages