r3wp [groups: 83 posts: 189283]
  • Home
  • Script library
  • AltME Archive
  • Mailing list
  • Articles Index
  • Site search
 

World: r3wp

[!REBOL3-OLD1]

Anton
7-Sep-2006
[1354x2]
I try to avoid using extra variables. They can be a real pain when 
it comes to optimization and make things look messier. Of course, 
when using a variable the argument order becomes less important. 
It's only important when no variables are used and specified directly.
... but I'm still passionate about these cases, because they happen 
often.
BrianH
7-Sep-2006
[1356]
The series function standard is
    function data-to-be-operated-on modfier-arguments

That's what I used with conjoin. It was also intentional that the 
data block not be reduced by conjoin. I see conjoin as an operation 
that you pipe data through, like utilities on Unix. If you want the 
data reduced, go ahead and do so - if not, don't.
Graham
7-Sep-2006
[1357x2]
reconjoin
lol
BrianH
7-Sep-2006
[1359x8]
Looking at your conjoin with the /only and /pad-only refinements, 
it seems that with the /only you are trying to recreate the delimit 
function, but not as usefully. I thought of using pad as a variable 
name, but "delimiter" was more appropriate since padding functions 
usually pad outside the data, not within it. Let me try to add you 
fixes to my version and see what I get.
delimit: func [
    "Put a value between the values in a series."
    data [series!] "The series to delimit"
    delimiter "The value to put into the series"
    /only "Inserts a series delimiter as a series."
    /copy "Change a copy of the series instead."
    /local
] [
    while either copy [
        if empty? data [return make data 0]
        local: make data 2 * length? data
        [
            local: insert/only local first data
            not empty? data: next data
        ]
    ] [
        local: data
        [not empty? local: next local]
    ] pick [
        [local: insert local delimiter]
        [local: insert/only local delimiter]
    ] none? only
    head local
]

conjoin: func [
    "Join the values in a block together with a delimiter."
    data [any-block!] "The series to join"
    delimiter "The value to put into the series"
    /only "Inserts a series delimiter as a series."
    /quoted "Puts string values in quotes."
    /local
] [
    if empty? data [return make data 0]

    local: tail either series? local: first data [copy local] [form :local]
    while [not empty? data: next data] either any-string? local [
        either quoted [
            local: insert tail insert head local {"} {"}

            [local: insert insert insert insert local delimiter {"} first data 
            {"}]
        ] [[local: insert insert local delimiter first data]]
    ] [pick [
        [local: insert insert local delimiter first data]
        [local: insert insert/only local delimiter first data]
    ] none? only]
    head local
]
In theory, the
    pick [false-val true-val] none? option

pattern should be faster for straight value return than the pattern
    either option [true-val] [false-val]
when no evaluation is required.


I used while on purpose instead of foreach since it doesn't rebind 
the block passed to it. That should speed things up too.
The pick pattern is also good for nested options, like this

    pick pick [[no-a-no-b no-a-yes-b] [yes-a-no-b yes-a-yes-b]] none? 
    a none? b
The copy refinement on the delimit function should be faster than 
pre-copying the data because the copy is preallocated to size.
If you want to try something really fun, pass a no-argument function 
value as the delimiter argument. You can use this for all sorts of 
tricks, though if you are doing that the references to delimiter 
in the conjoin function should be put in parentheses for safety. 
Like this:
conjoin: func [
    "Join the values in a block together with a delimiter."
    data [any-block!] "The series to join"
    delimiter "The value to put into the series"
    /only "Inserts a series delimiter as a series."
    /quoted "Puts string values in quotes."
    /local
] [
    if empty? data [return make data 0]

    local: tail either series? local: first data [copy local] [form :local]
    while [not empty? data: next data] either any-string? local [
        either quoted [
            local: insert tail insert head local {"} {"}

            [local: insert insert insert insert local (delimiter) {"} first data 
            {"}]
        ] [[local: insert insert local (delimiter) first data]]
    ] [pick [
        [local: insert insert local (delimiter) first data]
        [local: insert insert/only local (delimiter) first data]
    ] none? only]
    head local
]
I'm reasonably certain that these functions will fail awkwardly if 
the data contains unset! values. Should this be fixed?
Volker
7-Sep-2006
[1367x2]
Anton, "I try to avoid using extra variables". In my scenario its 
not an extra variable. I have a block of data from elsewhere, so 
it is in a variable. I want that nicely formed, with delemiters. 
If i want  the data inline, i need no conjoin, i can join it by putting 
all in one string.
I have problems to find a case where using it for inline-blocks makes 
much sense. Maybe i have a blackout here, can you give a non-artificial 
case?
Anton
8-Sep-2006
[1369x11]
Brian:

The series function standard is
    function data-to-be-operated-on 
modfier-arguments

Yes I understand, but this standard doesn't make sense to me. For 
comparison my preferred pattern is:
    function smaller-argument larger-argument
I've pretty much abandoned the DO/NEXT idea for now.
Actually, from my tests it looks like EITHER is faster than PICK 
.. NONE? :
>> time-it: func [iterations code /local t0 t1][t0: now/precise loop 
iterations code t1: now/precise print difference t1 t0]
>> time-it 4000000 [pick [[a][b]] none? true]
0:00:04.306
>> time-it 4000000 [either true [[a]][[b]]]
0:00:03.555
>> time-it 4000000 [pick [[a][b]] none? none]
0:00:04.266
>> time-it 4000000 [either none [[a]][[b]]]
0:00:03.525
... which surprised me a little bit. I think maybe the reason pick 
is favoured in general use is because there's less typing.
Brian, I think you might have misunderstood how I reworked the /only 
and /pad-only refinements, or I've misunderstood what you're trying 
to say about it. Let's consider /ONLY:

The first value in DATA is a block, so the result is a block. The 
second value 2 is inserted as is. The third value [3] is a block, 
but the contents are INSERTed, so the block is unwrapped:
	>> conjoin '| [["one"] 2 [3]]
	== ["one" | 2 | 3]


Same result except this time the /ONLY refinement causes the third 
value [3] to be inserted as is, so it remains a block:
	>> conjoin/only '| [["one"] 2 [3]]
	== ["one" | 2 | [3]]


This seems to me to be a necessary option in the treatment of the 
input data.
pad

 -> "delimiter"  -  I am forced to agree that "delimiter" is a better 
 word, I'll probably align with you on that, although it's hard to 
 go up to such a longer word again :)
Volker, very good question.......
If Volker is right, I think ramifications are I back down on several 
issues.
No, I have thought of a reasonable situation.
Example:
	conjoin/only '| reduce [[option1] option2 option3]
	== ['pigs | 'sheep | 'goats]


So the data block was specified inline, but not used directly because 
of the reduce, and no intermediate variable was used.
Volker
8-Sep-2006
[1380]
reduce [option1 '| option2 '| option3]
Does the same?
Anton
8-Sep-2006
[1381x3]
Yes, but you have to write all the delimiters.
That's what conjoin allows you to do - compress the common delimiter.
(imagine there were 30 options)
Volker
8-Sep-2006
[1384x3]
But in this example the delemiter-version is actually shorter. And 
if i have something really one, i can live with an extra var. I guess 
i would pull it out of the code anyway.
So i think its rare and acceptable. While i would use it a lot for 
printing a result-block nicely delemited.
really one -> really long
Anton
8-Sep-2006
[1387]
In this example the delimiter was very short and the data block was 
very short. Such short blocks can usually be processed manually in 
many different ways, so they are not good examples as is. You have 
to imagine much longer blocks.
Volker
8-Sep-2006
[1388x2]
But do such blocks happen often enough? IE more often then using 
conjoin to format results? I personally think not, but others may 
code different.
just conjoin results as CSV and show them in spreadsheet..
Anton
8-Sep-2006
[1390x3]
Maybe it's hard to think of the usages now, but, you know, it took 
a while to learn when and how to use rejoin.
How about this, imagine it's HTML code:
	conjoin/only table-divider reduce [table1 table2 table3]
Hmm....
Oldes
8-Sep-2006
[1393x2]
Will be rebocode available in R3?
(rebcode)
Ladislav
8-Sep-2006
[1395]
yes, according to http://www.rebol.com/notes/rebol3roadmap.html
Oldes
8-Sep-2006
[1396]
Just wanted to remind, that Rebcode was here for a short time:-) 
Hope it will come back:-)
Henrik
8-Sep-2006
[1397]
it better come back...
BrianH
9-Sep-2006
[1398x6]
Anton, good to know the timing. It could have gone either way so 
it's good to know which is faster. The either method is more compilable 
too (or translatable to rebcode if you prefer).
As for the word "delimiter" I made sure it wouldn't be exported to 
the external programming environment - no keyword arguments in REBOL, 
and I didn't use it for a function name or refinement. The more precise 
meaning of the word makes it a better choice for source that may 
serve as documentation, using the help or source commands.
I did understand your point about the /only and /pad-only refinements, 
but I realized that my delimit function made the change unnecessary, 
since its behavior was exactly what you were getting at. Using your 
example:
>> delimit ["one" 2 [3]] '|
== ["one" | 2 | [3]]
The behavior of your conjoin/only isn't really joining, it's more 
like aggregation. Your preference, I suppose.
Keeping the concepts distinct wasn't the only reason I made seperate 
"conjoin" and "delimit" functions - it's more efficient too.
I have switched my functions from pick to either. If you like I will 
post the changed versions.