REBOL3 tracker
  0.9.12 beta
Ticket #0002193 User: anonymous

Project:

Previous Next
rss
TypeWish Statuspending Date16-Feb-2015 18:41
Versionr3 master CategoryMezzanine Submitted byrebolek
PlatformAll Severityminor Prioritynormal

Summary Pass indexes to ARRAY/INITIAL function.
Description ARRAY/INITIAL accepts a function. I think that it should pass index to that function so the function would be much more useful.
Example code
Old code:

>> array/initial length: 10 func [] [-- length]
== [10 9 8 7 6 5 4 3 2 1]

New code:

>> array/initial 10 func [i] [i]
== [1 2 3 4 5 6 7 8 9 10]


Assigned ton/a Fixed in- Last Update2-Mar-2015 11:13


Comments
(0004565)
fork
18-Feb-2015 03:58

I've been experimenting with the idea that when functions are used in circumstances like this, that the parameter be via refinement... and whether the parameter is passed depends on if the refinement is in the spec.

The thing is that there might be a "stateful" function that already knows how many times it has been called, and *forcing* a parameter could complicate it to needing to write an adapter. So my idea is more like allow both:

>> array/initial length: 10 func [] [-- length]
== [10 9 8 7 6 5 4 3 2 1]

>> array/initial 10 func [/index i] [i]
== [1 2 3 4 5 6 7 7 9 10]

"Sniffing" for the capability to take a refinement is not something that is made particularly easy today, but as with many things, it could be made easier.

This is all ignoring the name "ARRAY" which is a separate question. :-)
(0004566)
BrianH
22-Feb-2015 08:09

ARRAY can generate multidimensional blocks, so the index passed should be either a direct index for single dimensions or a block of indexes for multiple dimensions; or, for usability, pass in the same number of parameters as there are dimensions using a composed argument to APPLY.

We will probably need to use APPLY anyway, since the current model isn't very op!-friendly, and we want the actual arity and evaluation model of the function's arguments to be ignored so our code is more robust. It gets trickier when you consider that the multi-dimensional arrays are composed with recursive calls to ARRAY, which would probably require some kind of intermediate function to be passed which curries parameters if we continue to do it this way. We should see if there's an efficient way to do this.

Still, this seems like a good idea. The way we handled this usage model was to have the passed function do a counter as a side effect. Something that works with pure functions would be preferable.

(The above was completely ignoring Fork's refinement suggestion, since APPLY can handle optional arguments without needing a refinement in the function.)
(0004567)
BrianH
23-Feb-2015 06:41

OK, I figured out a reasonably efficient implementation of this. It passes the indexes of the current element to the value function in the order specified with the size block, but calls that function with APPLY so the function doesn't necessarily have to take those index arguments, since APPLY ignores evaluation modes and arity, padding missing arguments with nones. It uses an internal function option for the recursive calls, but it enforces internal use of that option by using a bound tag word argument.

array: func [
    "Makes and initializes a series of a given size."
    size [integer! block!] "Size or block of sizes for each dimension"
    /initial "Specify an initial value for all elements"
    value {Initial value (will be called each time if a function)}
    /local block rest word
    /with tag indexes
] [
    unless same? :tag 'tag [with: tag: indexes: none]    ; Enforced internal
    if block? size [
        if all [not with any-function? :value] [
            indexes: append/dup make block! 2 * length? size [index? block] length? size
        ]
        if tail? rest: next size [rest: none]
        unless integer? set/any 'size first size [
            cause-error 'script 'expect-arg reduce ['array 'size type? :size]
        ]
    ]
    block: make block! size
    case [
        block? :rest [
            either any-function? :value [
                word: in make object! copy [x: block] 'x
                indexes: change next indexes word
                loop size [
                    set word insert/only get word array/initial/with rest :value 'tag indexes
                ]
                block: get word
            ] [
                loop size [block: insert/only block array/initial rest :value]
            ]
        ]
        series? :value [
            loop size [block: insert/only block copy/deep value]
        ]
        any-function? :value [
            unless indexes [indexes: [index? block]]
            loop size [block: insert/only block apply :value head indexes]
        ]
        insert/dup block value size
    ]
    head block
]

Here's an example of its use:
>> array/initial [3 4] func [x y] [ajoin [x " " y]]
== [["1 1" "1 2" "1 3" "1 4"] ["2 1" "2 2" "2 3" "2 4"] ["3 1" "3 2" "3 3" "3 4"]]
>> array/initial [3 4] does [random 100]
== [[30 79 47 79] [37 52 45 74] [23 36 60 86]]
(0004575)
BrianH
2-Mar-2015 11:13

PR: https://github.com/rebol/rebol/pull/235

Date User Field Action Change
2-Mar-2015 11:13 BrianH Status Modified reviewed => pending
2-Mar-2015 11:13 BrianH Comment : 0004575 Added -
23-Feb-2015 11:12 BrianH Comment : 0004567 Modified -
23-Feb-2015 06:48 BrianH Summary Modified Pass index to ARRAY/INITIAL function. => Pass indexes to ARRAY/INITIAL function.
23-Feb-2015 06:48 BrianH Code Modified -
23-Feb-2015 06:48 BrianH Category Modified Unspecified => Mezzanine
23-Feb-2015 06:48 BrianH Status Modified submitted => reviewed
23-Feb-2015 06:42 BrianH Comment : 0004567 Modified -
23-Feb-2015 06:41 BrianH Comment : 0004567 Added -
23-Feb-2015 06:28 BrianH Comment : 0004566 Modified -
22-Feb-2015 08:15 BrianH Comment : 0004566 Modified -
22-Feb-2015 08:09 BrianH Comment : 0004566 Added -
18-Feb-2015 03:58 Fork Comment : 0004565 Modified -
18-Feb-2015 03:58 Fork Comment : 0004565 Modified -
18-Feb-2015 03:58 Fork Comment : 0004565 Added -
16-Feb-2015 18:41 rebolek Ticket Added -