split (see the spec [here](http://www.rebol.com/r3/docs/functions/split.html)). Features wise, it looks good./into word as it has a strong meaning in other funcs. At first, I was expecting it to put the result into an existing serie, like collect./into?/part, but i'm not sure.split serie integer is not friendly understandable at first. If you don't use it frequently, you have to use help anyway./into wasn't a standard refinement when split was designed. I agree that it's confusing now and should be changed./part is really the default behavior for an integer dlm value. That seemed to be the more common case, compared to the /into behavior. If we reverse the default, /part could be used.cut is more like extract which I have also experimented with extending via a dialected interface.cut as a tool like *nix's command of the same name. cutis not the good name. Nor extract. They have the meaning of removing something or picking only part of.separate could be a candidate, but does it read good as a function ? separate [1 2 3 4 5 6] 2 is not very understandable at first, does it ?/parts has the right meaning but may be too close to /part and be confusing, because the behavior is different./pieces was considered in the past as well./in for the parts number ?in related to contexts? Also not as descriptive to me, aside from the single "split in two" case.rotate: function [ "Rotate values in a series." series [series!] /left "Rotate left (default)" /right "Rotate right" /part count [number!] "Rotate this many positions" ][ if empty? series [return series] ; modulo the count against the series size count: any [all [count count // length? series] 1] ;if zero? count [return series] ; MOVE handles a zero /part correctly head either right [ move/part (skip tail series negate count) series count ][ move/part series tail series count ] ] rotate-tests: [ blk: [1 2 3 4 5 6 7 8 9 10] rot: func [cmd] [prin [mold blk mold cmd "== "] do cmd print mold blk] rot [rotate blk] rot [rotate/left blk] rot [rotate/right blk] rot [rotate/right blk] rot [rotate/part blk 3] rot [rotate/right/part blk 3] ] ;do rotate-tests
rotate: function [
"Rotate values in a series."
series [series!]
/left "Rotate left (default)"
/right "Rotate right"
/part
count [number!] "Rotate this many positions"
][
count: any [count 1]
head either right [
move/part (skip tail series negate count) series count
][
move/part series tail series count
]
]/left, as it is the default one. Same as for the native shift (though it uses /right as default, so we should be consistent there).move/part is missing some bound checking:red>> a: [1 2] move/part a tail a 10 *** Runtime Error 1: access violation *** Cannot determine source file/line info. ***
? to be more strictly predicates (returning logic! results). There aren't too many, but some are common (index? length? type?, suffix?) others like sign?, brightness?, modified? and size? are important but less used I imagine.modified? being at the top of my list. I've used changed? or dirty? in the past for tracking changed items.? strictly on predicate funcs, and I've used an -of suffix as a substitute (file-of, path-of, etc) sign?'s implementation?sign?: func [
"Returns sign of N as 1, 0, or -1 (to use as a multiplier)."
n [number!] ; money! time!
][
either positive? n [1][
either negative? n [-1][0]
]
]
sign?: func [
"Returns sign of N as 1, 0, or -1 (to use as a multiplier)."
n [number!]
][
case [
positive? n [1]
negative? n [-1]
zero? n [0]
]
]
sign?: func [
"Returns sign of N as 1, 0, or -1 (to use as a multiplier)."
n [number!]
][
case [
n > 0 [1]
n < 0 [-1]
n = 0 [0]
]
]n as the param name above. remainder uses value1 value2 and modulo uses a b. The log* funcs use value. n makes sense when a param can only be a number, though even then we have some gray areas. When you include vector! and others, it's not as obviously clear. dividend and divisor would add any meaning to modulo (and it would make the implementation 10 times as long. default is a nice little func (Ladislav's idea IIRC) that made it into Rebol. If added to Red, I do have one behavior change to suggest. I think it should return the value the word refers to, whether it was set or pre-existing, rather than the value passed in (as Rebol does).default: func [ "Set a value for the word if the word is not set or is none." 'word value ][ if any [not value? :word none? get word] [ set word :value ] get word ]
incr. I mean, what could be easier?incr: function [ "Increments a value or series index." value [any-word! series! number!] "If value is a word, it will refer to the incremented value" /by "Change by this amount" amount [number!] ][ default amount 1 either any-word? :value [ op: either series? get value [:skip] [:add] set value op get value amount :value ;-- Return the word for chaining calls. ][ op: either series? :value [:skip] [:add] op value amount ] ]
incr variations through the years, arguing with myself whether using a lit-word! arg made calling code cleaner to update word references, or if it should only operate directly on numbers. There's no clear winner when sometimes I write things in a very imperative style, and other times I chain calls and use HOFs like map.red>> w: 1 == 1 red>> incr w == 2 red>> w == 1 red>> incr 'w == w red>> w == 2 red>> b: [1 2 3] == [1 2 3] red>> incr b == [2 3] red>> b == [1 2 3] red>> incr 'b == b red>> b == [2 3]
decr: function [ "Decrements a value or series index." value [any-word! series! number!] "If value is a word, it will refer to the decremented value" /by "Change by this amount" amount [number!] ][ incr/by value negate any [amount 1] ]
/into refinement. /parts is the best replacement name I've come up with so far. Here's a gist we can use for discussion: https://gist.github.com/greggirwin/66d7c6892fc310097cd91ab354189542collect in the gist, though that's fixed in newer Red versions. I also included map-each which is the first dependency we need to get approved for inclusion in Red. rejoin is trivial to replace with regular code, split is much less trivial. Also, rejoin, like all conversion functions, needs to be reconsidered in a global way before we do any implementation.rejoin will be gone, replaced with much improved join, not a single tear will be shed I guess.join function ;)action! values from the list, as they are part of the Core language.help action!;-)split makes it into Red, or if it never does, you can still include the code and use it yourself. apply :append [data 1 none none none true 3] (taken from http://www.rebol.com/r3/docs/functions/apply.html) I would prefer something like apply :append [data 1 /dup 3]apply, as internally, it still needs to construct the stack frame, filling the rest with nones. The compiler though, wouldn't have any performance issue with such form (if used literally, otherwise, same penalty as interpreter).appen example with three nones shows problems of current approach. It’s not something you can write without consulting documentation. Counting refinements **and** their args is not very intuitive.apply currently does, there is no work around that, as it is the stack frame required to call the function at the lowest level. There's a performance trick the Red interpreter does with paths when they are function calls with refinements, to avoid the huge cost of resolving it on each call, maybe it could be applied to apply too (though it requires a literal path for that...). Maybe, an apply/short form could be used as a hook by the interpreter to lower the cost of a *shorter* form.apply being there as well, but for high level code, the performance impact is unimportant, relative to understandability (either when reading or writing). Keep apply as it is if you want. I will write up some thoughts on it.refine: func [
[catch]
"Refines a function with the specified refinements. DO the result to call the func."
'fn "The function"
refinements [any-block!]
][
to path! compose [(fn) (refinements)]
]do refine find [last tail] "abcdeabcfg" "abc". But when you need to propagate them, it means either doing this:my-find: func [ser val opts [block!]] [do refine find opts ser val]
my-find: func [ser val /last /tail /local opts] [
opts: collect [
all [last keep 'last]
all [tail keep 'tail]
]
do refine find opts ser val
]refine find [serie value /part 10 /skip 2]
apply would still be justified as a low level alternative.entab/detab in your Accepted list. Experimenting.detab: function [ "Converts tabs in a string to spaces. (tab size 4)" string [any-string!] /size number [integer!] /leading "Change only leading tabs to spaces" ][ s: append/dup copy "" space any [number 4] parse string compose/deep [ some [change tab s (either leading [[thru newline]][]) | skip] ] string ] print mold s1: detab "a b c^-d e f g" print mold s2: detab "^-a b c^-^-d e f g^-" print mold s3: detab/leading "^-a^-^/ b^-^/ c^-^-d e f g^/^-"
entab: function [ "Converts spaces in a string to tabs. (tab size 4)" string [any-string!] /size number [integer!] /leading "Change only leading spaces to tabs" ][ number: any [number 4] parse string compose/deep [ some [change (number) space tab (either leading [[thru newline]][]) | skip] ] string ] print mold entab s1 print mold entab s2 print mold entab/leading s3
compose in there because before I rebuilt the changed value was not reduced.if. detab: function [ "Converts tabs in a string to spaces. (tab size 4)" string [any-string!] /size number [integer!] /leading "Change only leading tabs to spaces" ][ s: append/dup copy "" space any [number 4] parse string [ some [change tab s if (leading) [thru newline] | skip] ] string ] print mold s1: detab "a b c^-d e f g" print mold s2: detab "^-a b c^-^-d e f g^-" print mold s3: detab/leading "^-a^-^/ b^-^/ c^-^-d e f g^/^-" entab: function [ "Converts spaces in a string to tabs. (tab size 4)" string [any-string!] /size number [integer!] /leading "Change only leading spaces to tabs" ][ number: any [number 4] parse string [ some [change number space tab if (leading) [thru newline] | skip] ] string ] print mold entab s1 print mold entab s2 print mold entab/leading s3
/leading but when you write the IDE you may want it. ;^)/leading.change some tab s should do.some [change tab s] should do.detab: function [ "Converts tabs in a string to spaces. (tab size 4)" string [any-string!] /size number [integer!] /leading "Change only leading tabs to spaces" ][ s: append/dup copy "" space any [number 4] parse string [ some [any [change tab s] if (leading) [thru newline] | skip] ] string ] print mold s1: detab "a b c^-d e f g" print mold s2: detab "^-a b c^-^-d e f g^-" ;print mold s3: detab/leading "^-a^-^/ b^-^/ c^-^-d e f g^/^-" print mold s3: detab/leading "^-^-a^-^/ b^-^/ c^-^-d e f g^/^-" entab: function [ "Converts spaces in a string to tabs. (tab size 4)" string [any-string!] /size number [integer!] /leading "Change only leading spaces to tabs" ][ number: any [number 4] parse string [ some [any [change number space tab] if (leading) [thru newline] | skip] ] string ] print mold entab s1 print mold entab s2 print mold entab/leading s3
unique is not very fast and memory friendly, so it’s better to handle this at insertion. Therefore I have this function that I use very often when working with some data:expand: func [ "Append value to block only if not present" block value ] [ unless find block value [ return append block value ] ]
expand doesn't express the meaning very well to me, but the functionality is good. Could almost be a refinement on append if we found the right name. I'm loathe to overload append too much though.append would not be enough to handle a set, you need to overload all the modifying actions/natives. A deep reactive approach could achieve that, though a dedicated datatype would be much more efficient.inset: func [ "Append value to series if not present; otherwise return location where value was found." series [series!] value ][ any [find series value append series value] ]
inset is too close to insert though. alter function which is very similar, so maybe just adding a refinement to alter would be enough. I would avoid the removal by default and allow it using a refinement.expand was the first name that came to my mind and I agree that something better can be used.>> source alter
alter: func [
{If a value is not found in a series, append it; otherwise, remove it. Returns true if added.
(Modifies)}
series [series! port!]
value
][
found? unless remove find series :value [append series :value]
]alter/keep ?alter is named (though I've never liked the name), to indicate possible removal. The question is what the refinement name would be.inset was the "set" part as well as the meaning. It *sort of* works, but it was a quick idea, not thought out deeply.for in special cases. I think sometimes I avoid it too hard, using while as you point out. I also don't care for the C model myself. An interesting thing is that the discussion that led to this REP (long ago) came from Ladislav proposing a cfor func. It is so general that, internally, my test implementation actually converts many of the cases to cfor format of [init][test][bump] blocks, which is used if it can't delegate to one of the other native loops.=. I experiment. I also support from, as for [i from 5 to 10 step 2] [print i]. Two things taken together can justify =: it's used in a lot of other languages and in the context of the dialect it means "i will equal the following values as the loop is processed." I agree it's a bit ambiguous though.round to be that way, though we could easily have trunc floor ceil etc. The point is that the name tells you something, and it collect information. The catch, of course, is that they have to work intuitively, so you really do make things easier rather than harder.for can be easily compiled down to native loops (using direct support in compiler, or some form of macros).for as a potential learning tool as much as a useful construct.x..y could do, but actually, it would end up being no different than pair!... so why not use pair! to specify ranges (extending some natives to accept it, e.g.: copy series 1x2 , just food for thoughts) ;-) OTOH, I'm not against a new literal range type, for sake of richer dialecting support.split was also designed as a multi-tool. I actually just played with adding a couple more to it, based on other ways I often split things.x implies different axes to me, not different points on the same line.x..y as tuple, nice one. ;-)bounds func. The upside to bounds is that you can do more. e.g. I have keywords like today and tomorrow. Also, range wraps bounds so it isn't too bad just as a func, and is simple to do, the main work being collect [for v start end step [keep :v]]. Yes, that is one of the special cases where I use for.copy/deep to do this, and it already has /types. So another question is how clone would differ from that. Don't want too many things that are very subtly different.