[EMAIL PROTECTED] wrote:
> 
> Ladislav asked for some functional programming in REBOL, so I've
> come up with a challenge.
> 
  ...
> 
> 3.  Return a block of the strings present in this structure:
>     [[[3 "hello"] [6 "world"] [7 "from" "rebol" 21]]
>      [[["hi"] ["this" ['aword] "is"] "a"] "test"]]
> 
>     (answer is ["hello" "world" "from" "rebol" "hi" "this"
>                 "is" "a" "test"])
> 
> As it turns out, it isn't possible to do this in REBOL with
> purely functional code because there doesn't seem to be a way
> to prepend an element to a block in a non-destructive way.

I haven't seen any responses to challenge #3 (although my email
reading has been spotty, thanks to some turkey...) so the following
is my first posted stab at it.  Critiques on performance and style
are welcome, as I am still working on which ideas in PROLOG, LISP,
etc. are viable in REBOL and which need to be re-thought.

NB:  I consider these "functional" -- although local variables are
utilized -- because the locals are only initialized once and are
never REassigned.  This is well-known shorthand (e.g. "let" from
LISP/SCHEME) where

    foo: func [arg /local temp] [
        temp: (... some expression1 on arg ...)
        (... some expression2 on temp ...)
    ]

is just a syntactical (and likely a bit faster) variation of

    baz: func [arg2] [
        (... some expression2 on arg2 ...)
    ]

    foo: func [arg1] [
        baz (... some expression1 on arg1 ...)
    ]

because it uses local definition and expression evaluation instead of
(secondary) argument definition and function invocation to do the same
computation.

    ;
    ; transform a (not string!) series (block?, list?, ...) so that
    ; the first member is un-nested
    ;

    pinch: func [arg /local front] [
        either all [
            series? arg
            series? front: first arg
            not any-string? front
        ][
            pinch compose [(first front) (next front) (next arg)]
        ][
            arg
        ]
    ]

>> pinch [[["hello" 0] "world" 1] [2 "!" 3]]
== ["hello" 0 "world" 1 [2 "!" 3]]

    ;
    ; flatten a (not string!) series? by concatenating the first
    ; element of the pinched argument with the flattened remainder
    ; of the pinched argument
    ;
    ; non-series arguments, string arguments, or empty arguments are
    ; their own flattened selves
    ;

    flatten:  func [arg /local loc] [
        either all [
            series? arg
            not any-string? arg
            not empty? arg
        ][
            loc: pinch arg
            compose [(first loc) (flatten next loc)]
        ][
            arg
        ]
    ]

>> flatten [[["hello" 0] "world" 1] [2 "!" 3]]
== ["hello" 0 "world" 1 2 "!" 3]

    ;
    ; recursively create a result that consists of only the strings
    ; from the original argument 
    ;

    _onlystrings: func [arg] [
        either empty? arg [
            arg
        ][
            either any-string? first arg [
                compose [(first arg) (_onlystrings next arg)]
            ][
                _onlystrings next arg
            ]
        ]
    ]

    ;
    ; produce the block of strings (per challenge #3) by finding the
    ; strings in the flattened structure
    ;

    onlystrings: func [arg] [
        _onlystrings flatten arg
    ]

>> onlystrings [[["hello" 0] "world" 1] [2 "!" 3]]
== ["hello" "world" "!"]
>> onlystrings [[[3 "hello"] [6 "world"] [7 "from" "rebol" 21]]
[    [[["hi"] ["this" ['aword] "is"] "a"] "test"]]
== ["hello" "world" "from" "rebol" "hi" "this" "is" "a" "test"]

Thus I think it IS possible to do challenge #3 in REBOL.

-jn-

Reply via email to