World: r3wp
[Rebol School] Rebol School
older newer | first last |
Steeve 3-Jan-2009 [1199] | i have problems with my connexion, see you laters, nite... |
BrianH 3-Jan-2009 [1200] | Good night. |
[unknown: 5] 3-Jan-2009 [1201] | If you guys get some good mezzanines please post them on my Mezzanine thread. http://www.tretbase.com/forum/viewtopic.php?f=8&t=30Also, if anyone wants to make the current ones posted more efficient please do. |
Gregg 3-Jan-2009 [1202] | If you're looking for HOFs, and other advanced information, check out Ladislav's articles: http://www.fm.tul.cz/~ladislav/rebol/ |
Sunanda 3-Jan-2009 [1203] | Or http://www.rebol.org/view-script.r?script=hof.r |
Geomol 3-Jan-2009 [1204] | REBOL is so nice, that if you miss some cool feature found in some other language, like the MAP function, you can make it in a line of code. We should add a wiki with such cool things from other languages, and how you do it in REBOL. |
Steeve 3-Jan-2009 [1205x4] | As someone stated, what about a mezzanine thread for new proposal and discussions... ? thinking about [fold] (aka reduce) , i don't know if it's really intersting to have it as mezzanine, cause it's really short: fold y [1 2 3 4][x: x + y] x: 0 repeat y [1 2 3 4][x: x + y] |
wrong: >>x: 0 >>foreach y [1 2 3 4][x: x + y] | |
[foreach] is our HHOF (higher higher order function) | |
map: func [ [throw] "Evaluates a block for each value(s) in a series and returns them as a block." 'word [word! block!] "Word or block of words to set each time (local)" data [block!] "The series to traverse" body [block!] "Block to evaluate each time" /into "Collect into a given series, rather than a new block" output [series!] "The series to output to" ] [ unless into [output: make block! either word? word [length? data] [divide length? data length? word]] head foreach :word data compose [output: (:insert) output (to paren! body)] ] >> map x [1 2 3 4][x + x] == [2 4 6 8] >> map [x y][1 2 3 4][x + y] == [3 7] >> map/into [x y][1 2 3 4][x + y][10 11 12] == [3 7 10 11 12] But still a problem with the output var collision | |
Gregg 3-Jan-2009 [1209x2] | There are mezz groups in various places, but it does't look like we have a group for it here. Feel free to start one. Maybe add a checklist as well, for final versions and prioritizing. In any case, FOLD is not necessary, but nice for those that want it. REBOL lives in many worlds. One world is for imperative-minded folks, that don't know or care about HOFs and such, another is those that do. |
fold: func [ ; i.e. Haskell foldl (fold left). Same as 'reduce in Python? series [series!] fn [any-function!] /with value "starting value; used as accumulator" ][ if not with [value: pick series 1 series: next series] foreach item series [value: fn value item] ] sum: func [block [any-block!]] [fold block :add] product: func [block [any-block!]] [fold/with block :multiply 1] sum-of-squares: func [block [any-block!]] [ fold block func [x y] [x * x + y] 0 ] | |
Steeve 3-Jan-2009 [1211x2] | still on map, should be faster with /into len: to integer! either word? word [length? data] [divide length? data length? word] either into [insert/dup output none len][output: make block! len] head foreach :word data compose [output: (:change) output (to paren! body)] |
(no need of to integer!) | |
BrianH 3-Jan-2009 [1213x9] | I think the original, functional-style FOLD would be more useful for REBOL, as the REBOL-style one has been covered by foreach. Perhaps it should be done as a function that takes a block and words and returns a function though. I am not familiar with the Haskel equivalent, but I'd be surprised if it doesn't return a function. |
Here's a version of MAP with the /into semantics and no variable capture problems. I had to add a local function, though it is only created once per call. Binding issues prevent the use of a literal function. While i was at it I added unset! handling like the R3 version. map: func [ [throw] "Evaluates a block for each value(s) in a series and returns them as a block." 'word [word! block!] "Word or block of words to set each time (local)" data [block!] "The series to traverse" body [block!] "Block to evaluate each time" /into "Collect into a given series, rather than a new block" output [series!] "The series to output to" ] [ unless into [output: make block! either word? word [length? data] [divide length? data length? word]] foreach :word data reduce [func [val [any-type!]] [ if value? 'val [output: insert output :val] ] to paren! body] either into [:output] [head :output] ] | |
Note that the tail is returned with /into, for chaining purposes. | |
I forgot an /only though - the insert is supposed to be insert/only for the right behavior. | |
I forget /only more than I would like :( | |
Note that the copy performed by the FOREACH is a bind/copy, not a copy/deep. This means that only any-words, blocks and parens are copied, all other types are just referenced, including the function. | |
I think path! types are also copied by bind/copy. | |
Definitely not list! types. | |
Janko, the REBOL equivalent of Haskell's filter is REMOVE-EACH. | |
Janko 3-Jan-2009 [1222] | I read where I left off yesterday, very interesting chat, BrianH, thanks for info on remove-each |
BrianH 3-Jan-2009 [1223] | I added the last MAP with the insert/only fix to DevBase. We'll see if it gets accepted. All we're missing now is fold. |
Janko 3-Jan-2009 [1224] | great! :) |
Steeve 3-Jan-2009 [1225x2] | just a thing Brian... i don't like how map evolved. It lost his simplicity and inner speed. Some gain like (either vs to-block) have been over rated. some other bringing major speed regression have been under rated. i prefer the throw of an error during initialisation (ie. if find word 'output) instead of using the tricks of the embedded builded function. |
perhaps we should have 2 distinct foreach block or 2 distinct functions (map and map-into) | |
BrianH 4-Jan-2009 [1227x4] | We would have had to add the function anyway, for the the unset! screening we need for compatibility. And using the word 'output is not an error, so treating it as an error is inappropriate. |
This was a backport mezzanine, and for backports the compatibility with R3 is key. You have to do some extra work when writing mezzanine functions that you don't have to do with one-off functions. | |
The /into option only ended up adding the overhead of 2 compares (the unless and either, no overhead in the foreach), and one head when taken. The rest of the added code either reduced overhead (preallocation of the output), dealt with errors (like treating any word as an error), or handled compatibility fixes (the if value? stuff). | |
What is the major speed regression? | |
Gregg 4-Jan-2009 [1231] | I don't think it's good that /INTO changes the result to return the tail. |
BrianH 4-Jan-2009 [1232x5] | I am still debating that. It doesn't return the tail, actually, it returns the point after the insert, which if not the tail you otherwise can't know from the outside. The /into option is meant to be usable for chaining. You already have the point of insertion in the form of the reference you passed. You can get the tail and head easily. The only bit of info you can't get is the position after the insertion. |
The /into option is not just for MAP. It is intended to be added to a lot of series generating functions. The point of it is to allow those functions to optionally use INSERT semantics, so that we can make it easier to use buffers in a lot of places for memory savings. It's part of an overall strategy to reduce the memory overhead of REBOL code. INSERT semantics were chosen because INSERT is the most basic function here. CHANGE and APPEND can be implemented with INSERT (and REMOVE, HEAD and TAIL), but not so easily the other way. | |
If you are going to retrofit one series function onto all of the series generation functions, INSERT is the most general. | |
You would tend to use FOO/into using a different usage pattern than FOO. | |
The real benefits come from adding the /into option to REDUCE, COMPOSE, MOLD and FORM. We might add LOAD/into as well, but it remains to be seen whether it provides enough benefit there. | |
Gregg 4-Jan-2009 [1237x2] | I understand. I just think it's bad design to have the same function return a different location in the series based on a refinement. Whether they return the head, tail, or point of change, consistency is the thing. |
Unless, of course, the refinement is called /TAIL or something. :-) | |
BrianH 4-Jan-2009 [1239x4] | It doesn't return the tail. |
Strangely enough, the consistency here will be consistency of the /into option across multiple functions. | |
The /into option follows INSERT semantics. It doesn't return the tail, it returns the point after the insert, for chaining. | |
You already have the start of the insert, and you can get the head and tail. | |
Gregg 4-Jan-2009 [1243] | Again, I understand. And it may be that having /INTO available everywhere--with consistent behavior--will not be a problem. Carl's call. |
BrianH 4-Jan-2009 [1244] | Carl called for this already - that's why I'm doing it :) |
Gregg 4-Jan-2009 [1245] | My point is that it's often hard to remember subtle details like this, and it can screw you up. |
BrianH 4-Jan-2009 [1246x2] | Well, if it works consistently for a dozen functions, you'll find it easier to remember :) |
And then you can just say "use /into" and you will know how it works, regardless of the function. | |
Graham 4-Jan-2009 [1248] | Looks like this group has been hijacked! |
older newer | first last |