Author: pat665 Date: 23-july-2004
2.1. With /part and a length value
2.2. With /part and a position
6. 1007-Forever loop and wait 0.002?
7. 1008-Hexadecimal conversion
8. 1009-How to disable the Escape key?
10. 1001-Block or list which one is the best?
11. 1010-How to export a function from a context?
12. 1011-How to print source code?
13. 1013-First letter in uppercase
14. 1014-Filename with no accent?
15. 1015-Number of argument of a function?
16. 1016-Difference between same and equal?
17. 1017-Sorting an object based on one field?
19. 1019-Using modulo to retrieve x and y from a linear index?
20. 1020-How to search for files with wildcards?
24. 1025-Expands an object to add extra fields?
25. 1006-Fast evaluation of a logical expression?
26. 1026-Padding a value with leading zeroes or a specified fill character?
This is a collection of "good-to-know" information about Rebol programming. Some are mine, most are from the Rebol-List, books or articles about Rebol.
How to modify string content?
test: "abcd" change/part test "A" 1 test ; == "Abcd" test: "abcd" change/part test "A" 2 test ; == "Acd"
test: "abcd" change/part test "A" find test "c" test ; == "Acd" test: "abcd" change/part test "A" find test "b" test ; == "Abcd"
Purpose: getting the different values from a given set of values from the first till the last, and returning to the first value after that.
The days of the week
; the values are stored in a block days: reduce ['sunday 'monday 'tuesday 'wednesday 'thursday 'friday 'saturday] ; the first value is copied at the end of the block day: first days append days day ; 'select will give us the value in order for i 1 31 1 [ print day: select days day]
timediff: func [ "Substract one date from another returning a duration" a [date!] b [date!] ] [ add a/date - b/date * 24:00:00 subtract any [a/time 00:00:00] any [b/time 00:00:00] ] >> timediff 8-jun-2003/00:01 7-jun-2003/23:59 == 0:02 >> timediff 9-jun-2003/00:01 7-jun-2003/23:59 == 24:02
How to express value in Euro?
>> EUR$123'456,00 == EUR$123456.00 >> EUR$123'456,00 + 1 == EUR$123457.00
A way to minimize cpu usage in forever loops such as:
forever [time: do now/time]
Add a wait 0.002 or more. In a multitasking environment, you take what you can get. So a forever loop tries to eat up the cpu. Inserting a wait 0.002 (I found the number by experiment on Win/Lin two years ago) behaviour is normal. Especially when doing:
some-port-list forever [ wait/all compose [ (some-port-list) 0] ]
when I changed the 0 to 0.002 I had a high-performance, non-blocking IO-engine that turned into RUgby a few months later.
>> to-hex 24 == #00000018 >> skip to-hex 24 6 == #18 >> to-string skip to-hex 24 6 == "18" >> to-integer #18 == 24 >> type? #18 == issue! >> to-issue "18" == #18 >> to-integer to-issue "18" == 24
Is there a way to intercept or deactivate the Escape key? You can disable it.
system/console/break: false
How to use hash?
;definition h: make hash! 10 ;filling the hash ;it is important that pair value have different type. ;Example: >> for i 1 5 1 [ append h to-string random 10 append h random 10] == make hash! ["1" 2 "4" 10 "7" 6 "5" 10 "6" 1]
We can see that "2" have no entries...
>> select h "2" == none
and that "5" has got one.
>> select h "5" == 10
Imagine we have only used integers...
>> for i 1 10 1 [ append h random 10] == make hash! [5 2 10 6 7 2 3 2 4 5]
then 2 will have an "unwanted" entries
>> select h 2 == 10 ;Sorting hash ;One has to use the /skip refinement to sort hash according to the record size. >> sort/skip h 2 == make hash! ["1" 2 "4" 10 "5" 10 "6" 1 "7" 6]
> But I believe that lists and blocks are implemented > differently internally, but I'm ignorant as to how. > any comments on this?
Conceptually, a block is just a collection of values where the sequence is directly known (ie. like an "array" REBOL knows where the Nth value is). A list on the other hand ensures that each value knows where the previous and next values are (finding the Nth value in this case involves "walking" the list one value at a time).
The following test attempts to highlight the strength and weakness of each series! type (keep in mind that the memory cost of list! / hash! is higher than block!).
Test Data: one million numbers from one through to one million in a 6.75MB file.
Test Environment: Intel Pentium 3.0 GHz with 1GB of RAM.
Operations Per Second
Operation Block! List! Hash! ================== ========= ========= ========= first / last 1,961,614 2,038,423 1,936,023 pick / poke / at 1,844,334 504 1,846,456 find / select (1) 1,546,259 1,538,385 1,399,606 find / select (2) 52 51 1,402,559 foreach 13 20 19 insert 81 1,087,684 19 insert tail 1,032,956 861,769 723,152 change 1,572,834 1,569,881 1,307,086 head / tail / next 1,883,858 1,916,338 1,816,748 remove 80 1,493,110 54 remove tail 1,127,093 1,187,992 1,115,157
(1) First value in series (2) Last value in series
> Is there a command that can be used on a block and cannot be used on a list?
All action! which can work with block! can work also with list! and hash!.
But there are many native! and function! which are different:
1) set
l: make list! [1 2] b: make block! [1 2] set [a b] b print [a b] set [a b] l print [a b]
2) list! and hash! cannot be filled with code to execute. Every command which requires a block to be executed does not accept a list! or an hash!. Functions that require a block! for some parameters and do not accept a series are many dozens. The Core manual does give a good explanation of lists in the Values section and how they differ - I suspect you just missed it. See... http://www.rebol.com/docs/core23/rebolcore-16.html#section-2.8
ANSWER: by using SET! SET always define a global word.
private-room: context [ a: b: 2 set 'public-fn func [][print [a]] ] a: "Some text" print [a] ; ==Some text public-fn ; 2
The good trick to know is the echo command.
echo %//windows/desktop/system.txt probe system quit
To stop echoing use
echo none
value: "don't stand so close to me" change/part value uppercase copy/part value 1 1 >> ?? value value: "Don't stand so close to me"
deaccent: func [file-name [file!] /local accents replacements ][ accents: ["à" "é" "è" "ï" "ô" "ù" "ç"] replacements: ["a" "e" "e" "i" "o" "u" "c"] lowercase file-name foreach accent accents [ replace/all file-name accent pick replacements index? find accents accent ] ] ;; func print ["before " f] f: %Ebouriffée-àéèïôùç-àéèïôùç.jpg deaccent f print ["after " f]
nargs: func [ {Returns the total number of args of a function (with and without refinements)} f [word! lit-path!] /local argc f-blk f-ref f-sig ref-pos next-ref-pos ][ ;The total number or arguments argc: 0 ;We either have a path or a function ;If we have a path, we count the number ;of arguments of the supplied refinements first. either path? f [ ;Translate the path to a block f-blk: to-block f ;Is it a function? if not any-function? get/any first f-blk [throw make error! "Rugby error: invocation not on a function"] bind f-blk 'do ;The refinements used f-ref: next head f-blk ;the function signature f-sig: first get first f-blk ;Now get the number of arguments foreach refinement ;and add them to argc repeat ref f-ref [ ;Find the ref refinement ref-pos: find/tail f-sig to-refinement ref ;If succeed in the find if not none? ref-pos [ ;try to find the next one next-ref-pos: find ref-pos refinement! if not none? next-ref-pos [ argc: argc + ((index? next-ref-pos) - (index? ref-pos)) ];if not none next-ref-pos ];if not none? ref-pos ];foreach ref f-ref ];either path? f first clause [ if not any-function? get/any f [ throw make error! "Rugby error: invocation not on a function" ] f-sig: first get f ];either path? f second clause ;Add the number of function arguments argc: argc + -1 + index? any [ find f-sig refinement! tail f-sig ] ];nargs
nargs: func [f [word! path!] /local rslt loc? ref? args refs fn rf][ fn: either word? f [ get f ][ rf: copy next to-block f get first f ] args: copy [] refs: copy [] parse third :fn [ any [ set w word! ( either ref? [ insert tail last refs w ][ insert tail args w ] ) | m: refinement! ( ref?: true insert tail refs reduce [m/1 copy []] ) | skip ] to end ] return either rf [ rslt: 0 (length? args) + foreach a rf [ rslt: rslt + length? any reduce [select refs to-refinement a []] ] ][ length? args ] ]
Consider a simpler, analogous set of evaluations:
>> a: b: "xyz" == "xyz" >> same? a b == true >> c: reduce [a b] == ["xyz" "xyz"] >> d: reduce [a b] == ["xyz" "xyz"] >> same? c/1 d/1 == true >> same? c/2 d/2 == true >> same? c d == false >> equal? c d == true
or even *more* simpler (pardon the grammar! ;-)
>> p: "12" == "12" >> q: "12" == "12" >> same? p/1 q/1 == true >> same? p/2 q/2 == true >> same? p q == false >> equal? p q == true
These all illustrate the difference between SAME? and EQUAL? in that it is entirely possible to have two different series values whose corresponding elements are the same. In my first example above, as in your original post, two different REDUCE expressions over two different blocks will not produce THE SAME block, even if the content of those two blocks are the same. However the blocks are equal.
My second example breaks it down even further. Two different appearances in the input of
"12"
correspond to two different strings, even though the corresponding characters in those strings are equal (and same, being immutable).
Likewise, two different appearances of [a b] result in the creation of two different blocks, even though the corresponding words in those blocks are the same. Therefore, when we reduce those distinct but equal blocks, we get distinct but equal resulting blocks.
We can make a function that sorts series given a field name:
sort-object-series: func [ "Sorts a series of objects" series [series!] field [word!] ][ sort/compare series func[a b][lesser? get in a field get in b field] ]
Now we can do this:
sort-object-series dir 'date sort-object-series dir 'file_name
We want to code something like this (this doesn't work!)
foreach number [ 8 0 2 4 0 5][ if number = 0 [CONTINUE] print 100 / number ]
The trick is to use catch and catch/name , throw/name if you need to catch other stuff too.
foreach number [ 8 0 2 4 0 5][ catch[ if number = 0 [throw 'continue] print 100 / number ] ]
When I want continue I just put loop 1 [...] inside my loop:
>> repeat n 5 [loop 1 [if n = 3 [break] ?? n]] n: 1 n: 2 n: 4 n: 5 == 5
Notice the 3 has been skipped.
Of course, you could also use catch and throw this way.
>>repeat n 5 [catch [if n = 3 [throw] ?? n]]
n: 1 n: 2 n: 4 n: 5 == 5
In brief, N modulo X is in the range of 0, 1, 2 ... (X-1). To get values in the range of 1 to X one has to use 1 + modulo (N - 1).
This problem arise frequently when dealing with two dimensional objects like chessboard.
y\x 1 2 3 4 5 6 7 8 1 01 02 03 04 05 06 07 08 2 09 10 11 12 13 14 15 16 3 17 18 19 20 21 22 23 24 4 25 26 27 28 29...
Some expected values:
index-to-x 11 -> 3 index-to-x 8 -> 8 index-to-x 16 -> 8
Modulo cannot be used directly, because it returns 0 where one can expect 8. A solution is to use modulo on (index - 1) and then add 1.
for i 5 17 1 [ print [ i " smart-modulo 8 is " i - 1 // 8 + 1]]
Knowing X and index, Y is computed with
(index - X) / 8 + 1
size: 8 for i 1 size * size 1 [ x: i - 1 // size + 1 y: i - x / size + 1 print ["index: " i " x: " x " y: " y ] ]
Note: the expression (i - x / size) always gives an integer result here. It is not the general case, and normally to-integer would be required.
The point here is to move forward and backward with circular values.
values next prev 1 2 6 2 3 1 3 4 2 4 5 3 5 6 4 6 1 5
The next value is modulo + 1 The prev value is smart-modulo (x + N - 1)
N: 6 smart-modulo: func [x N][x - 1 // N + 1] for i 1 6 1 [ print ["i: " #(tab) i #(tab) i // N + 1 #(tab) smart-modulo (i + N - 1) N] ]
c-next: func[ "return next value from sorted circular integer list, such as 1 2 3 4 5 6" i [integer!] N [integer!] ][ i // N + 1 ]
c-prev: func[ "return prev value from sorted circular integer list, such as 1 2 3 4 5 6" i [integer!] N [integer!] ][ i + N - 2 // N + 1 ] for i 1 100 1 [ print ["i: " #(tab) i #(tab) c-next i N #(tab) c-prev i N] ]
Modulo is a function that returns the remainder of first value divided by second. In Rebol modulo is noted: remainder or //.
>> r: remainder 16 8 == 0 >> r: 16 // 8 == 0
Modulo is of type integer.
>> type? r == integer!
With find/match/any
foreach file read %. [ if find/match/any file %*.txt [print file] ]
Use a file not a string
Note that %*.txt is a file not a string!
print [to-integer system/stats / 1024 "KB"]
Rebol [ Title: "Build" Author: "Ladislav Mecir" Email: lmecir@mbox.vol.cz Date: 7/Apr/2003/19:02 File: %build.r ] comment [ ; COMPOSE isn't very comfortable ; Example: ; having a: [block a] ; to obtain something like [[[block a] (f: fail) | (f: none)] f] ; we have to write the following description ; for COMPOSE: probe compose/deep [ [ (reduce [a]) ([(f: fail) | (f: none)]) ] f ] ; == [[[block a] (f: fail) | (f: none)] f] ; Example written using BUILD probe build [ [ only a (f: fail) | (f: none) ] f ] ; == [[[block a] (f: fail) | (f: none)] f] ; BUILD uses only two keywords: INSERT and ONLY ; and acts on subblocks and parens ; if we used the INSERT keyword instead, ; we would have obtained: probe build [ [ insert a (f: fail) | (f: none) ] f ] ; == [[block a (f: fail) | (f: none)] f] ; to escape the INSERT or ONLY keywords, we can probe build [only 'only only 'insert] ; == [only insert] ; paths needn't be escaped probe build [insert/only] ; == [insert/only] ; to escape a whole subblock, i.e. to prevent BUILD to modify it do probe build [only [insert only]] ; == [[insert only]]] ; to escape a whole paren! i.e. to forbid BUILD to modify it do probe build [insert [(insert only)]] ; == [(insert only)] ] build: function [ {Builds a block. More comfortable, than COMPOSE} block [block! paren!] ] [result value position] [ result: make :block length? :block parse :block [ any [ 'insert position: ( set/any [value position] do/next :position insert tail :result get/any 'value ) :position | 'only position: ( set/any [value position] do/next :position insert/only tail :result get/any 'value ) :position | set value block! ( insert/only tail :result build :value ) | set value paren! ( insert/only tail :result build :value ) | set value skip ( insert/only tail :result get/any 'value ) ] ] :result ]
A modify function by Gabrielle Santilli I can't find a good name for it, so I'll just use MODIFY, but I'd rather make a function that was the complementary of SELECT:
modify: func [series key new-value] [ ; should maybe cause an error here? ; or add the key... if not series: find series key [return none] ; doesn't allow poking functions, but i think this way ; is much more convenient... ; notice that you don't need to check if it is a function series/2: new-value series/2 series/2 ]
That is very QAD but has a very nice behavior:
>> b: [test 1 test2 3] == [test 1 test2 3] >> modify b 'test2 4 == 4 >> b == [test 1 test2 4] >> modify b 'test func [v] [v + 1] == 2 >> modify b 'test func [v] [v + 1] == 3 >> b == [test 3 test2 4] >> increment: func [series key] [modify series key func [v] [v + 1]] >> increment b 'test == 4 >> b == [test 4 test2 4]
Just some quick notes...
1. you can do this:
>> original: context [a: 1 b: 2] >> template: context [a: b: c: none] >> probe changed: make template original make object! [ a: 1 b: 2 c: none ]
to update the original object based on a template.
2. you could just recursively apply this function to all the sub-objects... this way you get a copy of all the objects so you won't need the mold anymore.
Do is not the faster way to evaluate a logical expression
Example:
>> a: 0 == 1 >> b: [a = 0] == [a = 0] ; do is correct but is not the faster >> either do reduce b [print "a=0"][print "a<>0"] a=0 ; first is faster than do >> either first reduce b [print "a=0"][print "a<>0"] a=0 ; all is faster than do >> a: 1 == 1 >> either all reduce b [print "a=0"][print "a<>0"] a<>0
pad: func [ "Pads a value with leading zeroes or a specified fill character." val [string! number!] n [integer!] /with c [char!] "Optional Fill Character" ][ head insert/dup val: form val any [all [with c] #"0"] n - length? val ] >> pad/with 123 10 #"*" == "*******123" >> pad 123 10 == "0000000123"
Pad: function [ "Pads a value with leading zeroes or a specified fill character." Value [string! number!] Length [integer!] /Fill Character [char!] "Optional Fill Character" ] [Field] [ if not string? Value [ Value: form Value ] if none? Character [ Character: #"0" ] insert/dup Field: copy "" Character Length change/part Field head reverse Value length? Value head reverse Field ]
pad: func [ "Pads a value with leading zeroes or a specified fill character." Value [string! number!] Length [integer!] /Fill Character [char!] "Optional Fill Character." ] [ copy skip insert insert/dup clear "" any [Character #"0"] Length Value negate Length ]