Re: Euphoria optimizations etc.
- Posted by Ralf Nieuwenhuijsen <nieuwen at XS4ALL.NL> Nov 29, 1998
- 580 views
>Somehow the readability starts to fall apart. >In a long and complicated nested loop you'd >have to keep in mind that certain variables >were "special". I'd rather see s[i] in the code, >and know what it means, than see x, and >have to remember that x was connected to s in a magic way. >Especially when x itself could be subscripted, and when >in some cases we still need to explicitly subscript s >inside the loop. Yes, after seeing your example I must admit, it is *that* good for readability. However, it would allow for something that needs a pretty fancy trick currently. Take this for loop: for i = 1 to lenght (s) item = s[i] -- Some proccesing s[1] = item end for Now, what would happen when we decide we want to cut element i out of s. The lengt of s would change, and we would 1) skip an element 2) create a run-time error indexing an element at the end that doesnt exist. This brings me back to a suggestion (an idea stolen from the language ICON) I made a while ago. The 'each' keyword, which means repeat this statement for every element in the sequence that follows. Its one statement, like {1, 2, 3} + 1 where the interpreter is in full control over all the loops. This allows a lot of optimization, not only less and thus more readable code. Consider these examples: Reversion: d = "Each Example" s = {} s = prepend (s, each d) s is now "elpmaxE hcaE" Or, a positive routine: d = { 34, 34, -45, 3, 0, 39, -100 } if each d < 0 then each d = - each d end if d is now { 34, 34, 45, 3, 0, 39, 100 } As you see the loop-constructs are tied to the datatype rather than to a condition. What happens when we say something like this: s = "Euphoria" d = "ICON" each s = each d s is now "ICONoria" So, the statement is executed as long as the arguments are available. Now, consider the built-in routine find (), it only returns the first found matching element. So, here is the new find, that we can use: global function find (object x, sequence s) sequence result result = {} for index = 1 to length(s) do if not compare (x, s[index]) then result = append(result, index) end if end for return result end function However, notice that is finds *all* positions, eventhough we might not use them all. Consider this example that uses the new find routine. -- Getting input puts (1,"Please enter some text here..") text = gets (1) -- Proccesing input and displaying result puts (1, "The founded positions (maximum 4): \n") printf (1, "Space number %d was found at position %d \n", {each {1,2,3,4} , each find(' ',text) } ) Say, I entered "What's up doc?\n" as input. It will display: Space number 1 was found at position 7 Space number 2 was found at position 10 Say, I entered "E u p h o r i a\n" as input It will display: Space number 1 was found at position 2 Space number 2 was found at position 4 Space number 3 was found at position 6 Space number 4 was found at position 8 Why only the first four ? Because each {1,2,3,4} can only *generate* the values {1,2,3,4} afterwhich not all arguments are available and the interpreter continues with the next statement. Although we did not display the rest of the result, they were calculated. Therefor the new generator syntax, look at the find function now: global generator find (object x, sequence s) for index = 1 to length (s) do if not compare (s[index], x) then return index end if end for end generator Why is it called a generator ? Because it generates a result on request. Only on request. This was the line that called the generator find () : printf (1, "Space number %d was found at position %d \n", {each {1,2,3,4} , each find(' ',text) } ) The line will be executed for the amount of spaces found in text or for the maximum execution of 4. This is due to " each {1,2,3,4} " The first time it executes, it calls the generator find (). It start at the top, and initiates the for-loop in that generator. Now, the first space is found, its returned. And the printf-statement will be executed for the first time. However, it will now try to execute again. The " each {1,2,3,4} " now gives 2. But what does it do with the find-generator ? Will it call it again ? No, it will continue execution where it left off. What happens, when there are only 3 spaces in the the 'text' ? The 4th time the generator is called, it reaches the end of the generator and thus leaves it without returning a value. The line is not executed any more. Like find (), you could imagine an generator called 'for' generator for (atom a, atom b, atom step) while a <= b do return a a = a + step end while end generator No more messing with exit, weird-loop constructs, jumping several levels, etc. 'Each' generates as much results as it can, either out of a sequence, a sequence result of a function or the produced values of a generator. Now take a look at this statement: print (1, for (0,6,2) ) Displays: 0246 printf (1, "%d\n", { for (0,6,2) } ) Displays: 0 2 4 6 Here the a problem exists. What is we want to execute multiple statements a number of times, using the same mechanism. Indeed another loop-construct using 'every': every print (1, for (0,6,2) ) do puts (1, '\n' ) end every And voila, this too diplays the following: 0 2 4 6 Considering this, you must have noticed, the find-generator could be done much easier: generator find (object x, sequence s) return for (1,lenght(s),1) when not compare (x, each s) end generator See, that I here introduced another keyword. Its the keywords 'when' which is like an if-statement, however, there is only one small difference. Its only attached to one statement, is much smaller and, most important, does evaluate all the arguments of the statement attached: if not compare (x, each s) then return for(1,length(s), 1) end if Would only call the for-generator every time the two values matched. The when operator, needs valid arguments of its attached statement, and then decides wether or not the statement is executed. And now, the keywords recurse, which is like each, however, it will return all ATOMS rather than all elements. If you dont grasp this at once, look at this example of 'each' fist: s = { { 1, 2, 3 } , { 0, 0, 0 } , { 6, 7, 8 } } every print (1, each s) do puts (1, '\n') end every Displays: { 1, 2, 3 } { 0, 0, 0 } { 6, 7, 8 } every print (1, each ( each s ) ) do puts (1, '\n') end every Displays: 1 2 3 0 0 0 6 7 8 every print (1, recurse s) do puts (1, '\n') end every Displays precizely the same. An " each ( each x ) " statement, generates results from generated result. It gives the atom of an 2D Image for example. An " each ( each ( each x ) ) " gives the atoms of an 3D Image. However, there are situations where you want to have it recurse all the way, no matter how many levels. This is the default for most math operations in Euphoria for example. Now look at this example: recurse x = recurse x + 1 Because math operations can only be implied on atoms, this is the default already. If you find the above a bit misleading, consider that it doesnt *generates* a value, it generates a *pointer* to that value. But I'm still not done, another keyword: 'all' It is like each, however it tries all variants. Look at this statement: text = "L O L" printf (1, "%d found at %d" , { all {1,2,3,4} , each find (' ', text ) } ) Displays: 1 found at 2 1 found at 4 2 fount at 2 2 found at 4 3 found at 2 3 fount at 4 4 found at 2 4 found at 4 And I'll end this document with the new recursive reverse generator: generator reverse (object x) if sequence (x) then return reverse ( x[ each for(length(x),1,-1) ] ) else return x end if end generator I'll also promise this is the last time, I mentioned this. I feel this mail is the first (and thus last) mail where I really was able to explain which features of ICON I like, and what advantages they give us. Sorry for the long mail, Ralf