# apply

Definition: | sequence res = apply -- or --papply (object s, integer fn, object userdata={}) (object s, integer fn, object userdata={})
Description: | Apply a routine to every element of a sequence and in the case of apply() return a new sequence of the same size (or max(length(userdata[i])) when s is true). s : the sequence to map, or true/false fn : the (routine_id of a) procedure to invoke or function to use as converter userdata : an object optionally passed to each invocation of fn (see comments re the default), or if s is true/false then this specifies all the arguments.
pwa/p2js: | Supported.
Comments: | apply, when s is a sequence: fn should be a function that accepts at least one parameter, which gets each s[i]. The optional userdata value of {} is only is passed to fn if it needs it: should fn have no second parameter or the second parameter is optional (already has a default), it will not get passed the above default. In fact, fn can have more than two parameters, as long as the third and later are all defaulted parameters. Such handling proved necessary to allow things like sprint() to be used. Obviously if you pass a non-{} userdata that disagrees in any way with fn’s second parameter you will get an error. In contrast, and again obviously, even if the first parameter of fn is defaulted it will still get passed each s[i] in turn. Compatibility note: In the Euphoria version of apply, fn must always accept two parameters, and is always passed userdata as the second argument. Also, Euphoria does not allow an atom in s, or have papply(). apply, when s is true: fn should be a function that accepts length(userdata) parameters. if userdata[i] is an atom, it is passed to every call. if userdata[i] is length(1), then userdata[i][1] is passed to every call. otherwise (n:=)length(userdata[i]) must match all other non-atom/length(1) elements of it. fn is then invoked n times (can be 0), and always with exactly length(userdata) args. apply, when s is false: fn should be a function that can accept length(userdata[i]) parameters for all elements of userdata, and is passed each in turn, aka invoked length(userdata) times, without hindrance or modification, except as follows. Any atom or string elements of userdata are treated as/converted to a length-one sequence, which is then passed as a single argument to fn. Shorter elements of userdata will result in/require more naturally defaulted arguments than longer elements. papply() is identical, except that fn must be a procedure, and there is no return value.
Examples: |
    sequence s = tagset(5)
    ?s                      -- displays {1,2,3,4,5}
    ?sprint(s)              -- displays "{1,2,3,4,5}"
    ?apply(s,sprint)        -- displays {"1","2","3","4","5"}
    ?apply(true,sprint,{s}) -- displays {"1","2","3","4","5"}
    ?apply(true,sprintf,{{"%d"},s})  -- {"1","2","3","4","5"}
    papply(true,printf,{1,{"%d,"},s})  -- 1,2,3,4,5,
    function plusn(integer n=0, m=5) return n+m end function
    ?apply(false,plusn,{{1,2},{3},4,{}})  -- {3,8,9,5}
Implementation: | See builtins\pApply.e (an autoinclude) for details of the actual implementation.
See Also: | filter
Technicalia | Passing any atom value in s other than true or false triggers a fatal error. Judicious use of functional programming style techniques can both shorten code and greatly improve clarity. Careful design can completely eliminate many laboriously hand-written looping constructs, for just one example. Equally, however, pig-headed adherence to "pure functions" and excessive nesting can produce quite unreadable and almost impossible to maintain software. The simple act of regularly saving intermediate results in appropriately named local variables will often make code easier to follow and debugging ten times easier, although admittedly self-clobbering, ie/eg res = f(res), can sometimes improve performance significantly, but usually at some small cost to overall clarity and therefore debugging.