Rebol-Core-GTK

Author: pat665
Date: 23-july-2004

Contents

1. Introduction

2. 1002-Changing string

2.1. With /part and a length value

2.2. With /part and a position

3. 1003-Circular values

3.1. Example

3.2. Coding

4. 1004-Date difference

5. 1005-Euro

6. 1007-Forever loop and wait 0.002?

7. 1008-Hexadecimal conversion

8. 1009-How to disable the Escape key?

9. 1012-How to use hash?

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?

15.1. first version Maarten

15.2. 2d version Cyphre

16. 1016-Difference between same and equal?

17. 1017-Sorting an object based on one field?

18. 1018-Use of catch?

18.1. With catch

18.2. With loop 1

18.3. With catch again

19. 1019-Using modulo to retrieve x and y from a linear index?

19.1. The chessboard problem

19.2. Retrieving X

19.3. Retrieving Y

19.4. Testing

19.5. Circuler values

19.6. functions

19.7. Definitions

20. 1020-How to search for files with wildcards?

21. 1022-Available memory?

22. 1023-Better than compose?

23. 1024-A modify function?

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?

26.1. Other solutions


1. Introduction

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.

2. 1002-Changing string

How to modify string content?

2.1. With /part and a length value

test: "abcd"
change/part test "A" 1
test ; == "Abcd"

test: "abcd"
change/part test "A" 2
test ; == "Acd"

2.2. With /part and a position

test: "abcd"
change/part test "A" find test "c"
test ; == "Acd"

test: "abcd"
change/part test "A" find test "b"
test ; == "Abcd"

3. 1003-Circular values

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.

3.1. Example

The days of the week

3.2. Coding

; 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]

4. 1004-Date difference

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

5. 1005-Euro

How to express value in Euro?

>> EUR$123'456,00
== EUR$123456.00
>> EUR$123'456,00 + 1
== EUR$123457.00

6. 1007-Forever loop and wait 0.002?

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.

7. 1008-Hexadecimal conversion

>> 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

8. 1009-How to disable the Escape key?

Is there a way to intercept or deactivate the Escape key? You can disable it.

system/console/break: false

9. 1012-How to use hash?

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]

10. 1001-Block or list which one is the best?

> 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

11. 1010-How to export a function from a context?

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

12. 1011-How to print source code?

The good trick to know is the echo command.

echo %//windows/desktop/system.txt
probe system
quit

To stop echoing use

echo none

13. 1013-First letter in uppercase

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"

14. 1014-Filename with no accent?

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]

15. 1015-Number of argument of a function?

15.1. first version Maarten

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

15.2. 2d version Cyphre

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
]
]

16. 1016-Difference between same and equal?

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.

17. 1017-Sorting an object based on one field?

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

18. 1018-Use of catch?

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
]

18.1. With catch

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
        ]
]

18.2. With loop 1

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.

18.3. With catch again

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

19. 1019-Using modulo to retrieve x and y from a linear index?

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).

19.1. The chessboard problem

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...

19.2. Retrieving X

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]]

19.3. Retrieving Y

Knowing X and index, Y is computed with

(index - X) / 8 + 1

19.4. Testing

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.

19.5. Circuler values

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] ]

19.6. functions

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] ]

19.7. Definitions

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!

20. 1020-How to search for files with wildcards?

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!

21. 1022-Available memory?

print [to-integer system/stats / 1024 "KB"]

22. 1023-Better than compose?

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
]

23. 1024-A modify function?

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]

24. 1025-Expands an object to add extra fields?

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.

25. 1006-Fast evaluation of a logical expression?

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

26. 1026-Padding a value with leading zeroes or a specified fill character?

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"

26.1. Other solutions

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
]