• Home
  • Script library
  • AltME Archive
  • Mailing list
  • Articles Index
  • Site search
 

World: r4wp

[!REBOL3] General discussion about REBOL 3

Ladislav
15-Mar-2013
[2071x2]
My tests are actually "preconditions" at least in the specification, 
but due to the overflow possibility I converted them to one precondition 
and one postcondition to be able to handle the arithmetic issues 
before they occur.
once the condition allows another evaluation, I already know that 
the overflow does not occur and I can increment without taking any 
risk
Gregg
15-Mar-2013
[2073x3]
You lost me. Do you mean CFOR can't spare the effort to do the checks, 
or you can't spare the effort to create a version of FOR that uses 
CFOR?
If Brian is OK with your new FOR, I think I am as well. I am also 
happy to refine my new LOOP func more, if people think there is value 
in it; as an alternative.
And I assume you will add support for other datatypes back into your 
FOR, beyond integers.
Ladislav
15-Mar-2013
[2076]
You lost me.
 - sorry, this looks to be caused by my bad English this time
 Do you mean CFOR can't spare the effort 

 - actually, CFOR (its implementation) does not need to handle such 
 issues leaving that to the programmer to handle only when needed


regarding the "I do not think it could spare any effort" - the sentence 
should probably look as follows: "I do not think it (implementing 
FOR using CFOR) could save any effort"
Gregg
15-Mar-2013
[2077]
Thanks for clarifying. And rememeber how much better your English 
is than my Czech. :-)


My LOOP, using CFOR, is slower to set up, but much faster in the 
loop than FOR (incl your new FOR), so I thought it might be worth 
it.
Ladislav
15-Mar-2013
[2078x2]
aha, interesting, did not make any measurements
Would not have guessed that to be the case. Then it really may be 
worth trying.
Gregg
15-Mar-2013
[2080]
loop - 1 - 100'000 calls 0:00:02.108
for - 1 - 100'000 calls 0:00:00.508
loop - 1'000'000 - 1 call 0:00:00.304
for - 1'000'000 - 1 call 0:00:01.122
Ladislav
15-Mar-2013
[2081]
I suppose you use R2?
Gregg
15-Mar-2013
[2082x10]
Yes.
Let me see if I can test real quick with R3. My timing func uses 
LOOP. :-)
R3 results:
loop - 1 - 100'000 calls 0:00:01.539
for - 1 - 100'000 calls 0:00:00.683
loop - 1'000'000 - 1 call 0:00:00.358
for - 1'000'000 - 1 call 0:00:01.144
No special handling in mine for decimal step values. So under R3, 
that test will show this:
[loop [i 0 1 0.2] [print i]]
0
0.2
0.4
0.6000000000000001
0.8
1.0
For those not following closely, the discussion about FOR is trying 
to clarify and document its behavior, while keeping it easy to use 
(e.g., avoid accidental infinite loops). Ladislav has also asked 
for better names for CFOR, his general loop func that is efficient 
and flexible, but requires a bit more care in use. There is also 
a general consensus that FOR takes more args than we would like, 
while noting that we have other looping funcs we can use in most 
cases.


I propose a new LOOP implementation that is compatible with the current 
LOOP func (and delegates to it internally), while also providing 
a dialected interface which is a wrapper to CFOR.
Goals:

* Provide a general loop that is both friendly and flexible
* Support multiple datatypes as FOR does today
* Use CFOR internally for efficiency
* Adhere to the latest model discussed here and on CC
Interface:

LOOP compatible
    [loop 5 [print "x"]]
REPEAT compatible
    [loop [i 5] [print i]]
CFOR compatible (except for all args being in a block)
    [loop [[i: 1] [i <= 1000] [i: i + i]] [print i]]
FOR compatible (except for all args being in a block)
    [loop [i 5 10 2] [print i]]
Like FOR, but with a default bump of 1
    [loop [i 5 10] [print i]]
Or -1 if start is greater than end
    [loop [i 10 5] [print i]]
I've posted %mezz/new-loop.r here, but the implementation should 
be considered preliminary, as it was meant mainly to experiment with 
the interface, generating CFOR calls, and analyzing the new behavior 
spec in terms of test cases. It can and should be improved.
You don't need to look at %new-loop.r, but if you have any input 
on the above interface approach, or just want to post a NEW-LOOP: 
+1, -1, or 0 message, that would be great.
Note that the above examples are, themselves, in block, because I 
cut them out of my test cases.
Sunanda
15-Mar-2013
[2092]
Nice work, Gregg - thanks for doing all that.


I am having trouble getting LOOP to do what  FOR can do with a series:
    ser: [1 2 3 4 5 6 7]
    for v ser skip ser 5 2 [print v]
        1 2 3 4 5 6 7
        3 4 5 6 7
        5 6 7

Neither of these work for me:
    loop [v ser skip ser 6 2] [print v]
    loop compose [v ser (skip ser 6) 2] [print v]
Gregg
15-Mar-2013
[2093x2]
Good catch. I just added series support, and since it's a simple 
dialect, it won't like that. In the current version, you would have 
to use an interim var for 'end. e.g.:

>> b: (skip ser 6)
== [7]
>> loop compose [v ser b 2] [print v]
One of the downsides of dialects, and wanting to keep them simple.
Sunanda
16-Mar-2013
[2095]
Thanks Gregg.

If FORing on a series is relatively uncommon, then (per curecode 
#666) losing the direct ability would be a good R3ish thing to do.


I am little more concerned about LOOP set up needing COMPOSE in a 
way that existing looping constructs do not. The cost of creating 
simplicity in the language core should not be exporting complexity 
to the language users.
Marco
16-Mar-2013
[2096x4]
my contribution to loop-for discussion:
	for-step: func [
		{simplified for}
		[catch]
		'word [word!]
		start [number!]
		end [number!]
		step [number!]
		body [block!]
		/local op result
		][
		do reduce [to set-word! :word start]
		if step = 0 [throw make error! "step parameter cannot be = 0"]
		op: either step > 0 [:lesser-or-equal?][:greater-or-equal?]

  while [op get word end][set/any 'result do body set word (get word) 
  + step] get/any 'result
	]
	for-step i 1 3 1 [print i]
another contribution:
	use [count inc start end op][
		count: inc: start: end: op: 0	
		in-range: func [
			[catch]
			'word [word!]
			start [number!]
			end [number!]
			/bump step [number!]
			/local result
			] [
			if inc = 0 [

    if step = 0 [throw make error! "step parameter cannot be = 0"]
				count: start

    either start > end [inc: -1 op: :greater-or-equal?][inc: 1 op: :lesser-or-equal?]
				unless none? step [inc: step]
			]
			set word count

   result: either op count end [count: (get word) + inc true][false]
			if not result [count: inc: start: end: op: 0]
			result
		]
	]
	i: 0 ; define a var
	while [in-range i 1 3] [print i]
about the Gregg's loop (on Rebol 2.7.8.3.1):
>>  probe cfor [num: 1] [num <= 3] [num: num + 1] [print num "a"]
1
2
3
4

??


>> probe cfor [num: 1] [num <= 3] [num: num + 1] [if num = 2 [throw 
make error! "what 2?"] "a"]
; nothing printed nor error report
what about this?:
	cfor: func [
		{General loop}
		[throw catch]
		init [block!]
		test [block!]
		inc [block!]
		body [block!]
		/local result
		] [

  do init while [do test] [set/any 'result do body do inc] get/any 
  'result
	]
BrianH
16-Mar-2013
[2100x2]
The existing LOOP is used quite often, so any replacement for it 
won't go in R3 by default. However, the main reason LOOP is used 
is because it doesn't have the overhead that a lot of the other loops 
have, less than the other natives even. Its simplicity and definite 
form are its strengths - a loop with a more flexible form would be 
need to process that flexibility at runtime, which would add inefficiency 
that could easily be avoided by making that choice at development 
time by choosing the loop that meets your needs. And any loop construct 
that requires any kind of manual reducing of its arguments in order 
to have it take the result of an expression is a definite no-go. 
I just got rid of that in REWORD.


I like http://issue.cc/r3/884as a replacement for FOR. It keeps 
the local binding (unlike Marco's CFOR above, sorry) and is flexible 
in behavior without being flexible in form (it has a very simple 
implementation).
Watch out though, all mezzanine control structures that execute code 
blocks passed as parameters are be subject to http://issue.cc/r3/539
so they would need to be native until we have a solution to that. 
And not a command, because the necessary internal stuff isn't exported 
(purposely), so you couldn't do REWORD as a command.
Marco
16-Mar-2013
[2102]
In rebol 2.7.8.3.1 the #884 version has the same "strange behaviors" 
as the one that Gregg posted as a file here.
BrianH
16-Mar-2013
[2103]
I just updated it to fix a couple more bugs; haven't read any of 
Gregg's comments so I don't know what those strange behavior are, 
but they might be fixed now. I am more a fan of its API style - we 
can fix any internal problems.
Marco
16-Mar-2013
[2104]
ok, much better now, just add [catch] at the beginning and it will 
catch also errors.
BrianH
16-Mar-2013
[2105]
No need, because this is an R3 function and R3 doesn't have [catch], 
it has better errors with stack traces instead. Improvement.
Marco
16-Mar-2013
[2106]
Nice! I must try it sooner then later ;)
BrianH
16-Mar-2013
[2107]
I had to get rid of [catch] in my first edit. And [catch] is a bad 
idea for loops because it hides where the real error is being triggered.
Marco
16-Mar-2013
[2108]
on Rebol 2.7.8.3.1 :
>> do [cfor [num: 0] [num <= 3] [num: num + 1] []]
** ERROR: ret has no value 
** Near: :ret
BrianH
16-Mar-2013
[2109]
No, that's legit. The body was an empty block. The result of evaluating 
an empty block is #[unset!]. So, the return value is #[unset!].
Marco
16-Mar-2013
[2110]
on Rebol 2.7.8.3.1 :
>> do [cfor [num: 0] [num <= 3] [num: num + 1] [num]]
** ERROR: ret has no value 
** Near: :ret
BrianH
16-Mar-2013
[2111x4]
Wait, R3 rules, :ret is get/any 'ret in R3. Change it to run in R2.
Why are you testing this in R2? It's written for R3.
I would have to change it for R2/Forward.
In R3:

>> cfor [num: 0] [num <= 3] [num: num + 1] [num]
== 3
>> unset? cfor [num: 0] [num <= 3] [num: num + 1] []
== true
Marco
16-Mar-2013
[2115]
Better to test it in R2 then to not test it at all. (By the way on 
R2 mine is a little faster).
I changed :ret to get/any 'ret and it works but in R3 :
>> do [cfor [num: 0] [num <= 3] [num: num + 1] [num]] ; works?


and why is it important to keep the local binding ?(I am not an expert 
of binding)
BrianH
16-Mar-2013
[2116x3]
Yours is faster because it doesn't do the binding. That binding is 
necessary.
The entire difference between CFOR and WHILE is that local binding. 
If you didn't need the local binding you should use WHILE.
Benefits to the local binding:

- You define new words that go away when the function ends, *if you 
want them to*

- The context created is an object context, which makes word lookup 
faster (O(1) instead of O(n))

- The context created can be references safely after the function 
ends

- All series in the loop body are copied, which makes them safe to 
modify during and after the loop, making binding loops even more 
task and recursion safe than non-binding loops.
Gregg
16-Mar-2013
[2119x2]
Sunanda, agreed on not export complexity. Words are supported directly, 
and we can look at making everything easy that it should support. 
Today, words are supported. e.g.:

a: 1
b: 5
loop [i a b 1] [print i]


Series values, as in your first bug report, are the thing I have 
to look into. Beyond that, should it just evaluate everything it 
gets?


Marco, FOR-STEP sounds too close to FORSKIP to me. Have to think 
about how FORSKIP fits in to the model. For that, and IN-RANGE, the 
main question is what their purpose is. On your first CFOR tests, 
I get these results:

>> probe cfor [num: 1] [num <= 3] [num: num + 1] [print num "a"]
1
2
3
4
== 4

>> probe cfor [num: 1] [num <= 3] [num: num + 1] [if num = 2 [throw 
make error! "what 2?"] "a"]
** Throw Error: ** User Error: what 2?
** Near: throw make error! "what 2?"
Brian, on efficiency, LOOP (right now) just does this at the top:

  either integer? spec [native-loop spec body] [...


So that's all the overhead there is to delegate to the native loop.