Hi Rebols

Here is a fast general map function which handles nested blocks
(tree-structured data). It applies any function of one argument to the
elements of a block. The deep refinement carries the mapping into nested
blocks. Without /deep, any nested blocks are simply copied unaltered. I
wrote this function last November as one of my first REBOL programs, and
thought perhaps others might find it useful.

I spent a lot of time thinking about how to pass functions as arguments. I
did not like the common method of passing it as a literal function body.
Then the user has to do things like (map :sine blk). But if you wanted
the sine using radians this syntax won't work, you have to say map
 'sine/radians blk). BTW I do not think this is mentioned in any of the
documentation. Also, if you invoke the passed function inside the routine
you would need to use :f again and also problems with refinements.

So what I came up with was to put the :f in the spec. That way neither the
user of the map function nor the author inside the map function needs to do
anything special and there is no problem with refinements as long as the
function has a single argument. All the ugly syntax is put in the spec. But
when we do this, it is no longer possible to pass in a literal function
body. A simple work-around is:

if paren? :f [f: f]       ;most interesting line in MAP

This allows a literal function body to be passed in parenthesis. I debated
whether to use a block wrapper, but decided to use parens to signify that
the function would be evaluated in the function. Also there is less
confusion of the wrapper () with the [] used in the function definition. If
one prefers to pass the function in a block, the line becomes:

if block? :f  [f: do f]

Any comments or discussion would be welcome.

Cheers
Larry

==============Examples==============

>> blk: [1 2 3 [4 5 [6]]]
== [1 2 3 [4 5 [6]]]

>> map to-string blk
== ["1" "2" "3" [4 5 [6]]]

>> map/deep to-string blk
== ["1" "2" "3" ["4" "5" ["6"]]]

>> m: [[1 2 3][4 5 6][7 8 9]]
== [[1 2 3] [4 5 6] [7 8 9]]

>> map/deep sine m
== [[1.74524064372835E-2 3.4899496702501E-2 5.23359562429438E-2]
[6.9756473744125
3E-2 8.71557427476582E-2 0.104528463267653] [0.121...

>> map/deep sine/radians m
== [[0.841470984807897 0.909297426825682 0.141120008059867]
[-0.756802495307928 -
0.958924274663138 -0.279415498198926] [0.656986598...

>> map/deep (func [x][x * x]) m
== [[1 4 9] [16 25 36] [49 64 81]]
>> m: [[9 1 8 2 7] [4 10 5 7 10] [5 10 6 7 7] [6 6 5 5 9] [8 3 10 3 4]]

>> map/deep even? m
== [[false true false] [true false true] [false true false]]


===============CODE===============

REBOL [
    Title: "Map Function"
    Date:  "24-November-1999"
    Author: "Larry Palmiter"
    Purpose: { Maps a function of one arg
onto a (nested) block of values.
    }
]

map: func [
    "Maps a function onto elements of a block"
    :f               "function (use parens for literal)"
    b [block!]    "block of values"
    /deep         "maps function into nested blocks"
    /local out
][
    out: make block! length? b
    if paren? :f [f: f]
    foreach el b [
        either block? el [
            either deep [
                append/only out map/deep f el
            ][
                append/only out el
            ]
        ][
            append out f el
        ]
    ]
]

Reply via email to