9
\$\begingroup\$

As the June 2021 Language of the Month is Red, what are some general tips for golfing in Red language?

Please suggest tips that are at least somewhat specific to Red (e.g. "remove comments" isn't a valid tip here).

Please one tip per answer!

\$\endgroup\$

9 Answers 9

7
\$\begingroup\$

Type casting:

In Red you usually use to <type> <spec> or the predifined to-<type> value functions:

>> to integer! 3.14
== 3
>> to-integer 2.71
== 2
>> to char! 65
== #"A"
>> to-integer #"A"
== 65

<type> can be a datatype or an example value (a prototype). That's why we can shorten the above examples as follows:

>> to 1 3.14
== 3
>> to 0 2.71
== 2 
>> to sp 65   ; sp is space = #" " 
== #"A"
>> to 1 #"A"
== 65

Of course this can be applied to all datatypes that have literal representation:

>> to[]123 ; to block! 123
== [123]
>> to""[123] ; to-string [123]
== "123"
>> to 1-1-1[2 6 2021]; to-date [2 6 2021]
== 2-Jun-2021
>> to now[2 6 2021]
== 2-Jun-2021
>> to 0.0.0[255 0 0] ; to tuple! [255 0 0]
== 255.0.0
>> to sky[255 228 196] ; sky is a predifined color 164.200.255 (a tuple!)
== 255.228.196
>> to 1x1[-10 42]; to-pair [-10 42]
== -10x42
>> to#{}"string"; to-binary "string"
== #{737472696E67}
\$\endgroup\$
3
  • 2
    \$\begingroup\$ In the case of converting a string to an integer, you can do do"123", which evaluates the string as code. \$\endgroup\$ Commented Jun 8, 2021 at 7:22
  • \$\begingroup\$ @dingledooper Yes, of course! Thank you pointing this out! Why don't you add a separate answer that covers do? \$\endgroup\$ Commented Jun 8, 2021 at 7:24
  • 1
    \$\begingroup\$ Ah, fare enough, will do. \$\endgroup\$ Commented Jun 8, 2021 at 18:03
6
\$\begingroup\$

String conversion with do

do is Red's equivalent of an eval command. It evaluates code as a string. It is useful when wanting to convert a string to an integer (or any valid Red expression):

to-integer n  # The obvious way
to 0 n        # From @GalenIvanov's type casting tip
do n          # `do` is shortest
\$\endgroup\$
4
\$\begingroup\$

Flatten a list of lists:

form the block into a string (form returns a user-friendly string representation of a value), then load the string (load returns a value or block of values by reading and evaluating a source)

>> b: [1 [2 3] [4 [5 6] 7]]
>> form b
== "1 2 3 4 5 6 7"
>> load form b
== [1 2 3 4 5 6 7]

>> b: [a b [c d [e f [g]]]]
== [a b [c d [e f [g]]]]
>> form b
== "a b c d e f g"
>> load form b
== [a b c d e f g]
\$\endgroup\$
4
\$\begingroup\$

The many uses of collect and keep

collect's help page shows the following:

USAGE:
     COLLECT body

DESCRIPTION: 
     Collect in a new block all the values passed to KEEP function from the body block. 
     COLLECT is a function! value.

ARGUMENTS:
     body         [block!] "Block to evaluate."

REFINEMENTS:
     /into        => Insert into a buffer instead (returns position after insert).
        collected    [series!] "The buffer series (modified)."

basically, collect takes all values passed to keep within its argument block and returns a series with those return values.

Effectively, it works as a way in which you can apply any block of code to a series and collect results from it.

You can use it as a map:

collect[foreach i arr[keep i + 1]] (increments each array element)

a filter:

collect[foreach i arr [if i < 2 [keep i]]] (keeps all array elements less than 2)

and much more. It finds use in any general place where you need to gather values for a code golf question. I have used it in this answer as a flatmap, for example.

\$\endgroup\$
3
\$\begingroup\$

More on type casting

Another non-universal alternative to type conversion is to use some arithmetic operation on the identity value (in the desired type) for the operation and the value to convert:

integer! to float!:

>> to-float 5
== 5.0
>> to 0.0 5
== 5.0
>> 0.0 + 5
== 5.0

char! to integer!:

>> to-integer #"A"
== 65
>> to 0 #"A"
== 65
>> 0 +#"A"
== 65

integer! to pair! (a pair with equal x and y values)

>> as-pair 5 5
== 5x5
>> to-pair[5 5]
== 5x5
>> as-pair 5 5
== 5x5
>> to 1x1 5
== 5x5
>> 1x1 * 5
== 5x5

integer! to tuple! (a tuple with all parts equal)

>> to-tuple[255 255 255]
== 255.255.255
>> to 0.0.0[255 255 255]
== 255.255.255
>> 0.0.0 + 255
== 255.255.255

integer! or float! to percent!:

>> to-percent 12
== 1200%
>> to 1% 12
== 1200%
>> 0%+ 12
== 1200%
\$\endgroup\$
3
\$\begingroup\$

Use any to provide a default value

Some functions (find, select and so on) could return none. The long way to structure your code around their result is to use conditionals - if, either etc.

Let's find the number of occurences of each character in the string "Tips for golfing in Red". h is a map! that wiil hold the frequencies. The verbose way is to use either key[true-code][false-code] - if the key exists do true-code, otherwise do false-code :

h: #()
s: "Tips for golfing in Red"
foreach c s[h/:c: 1 + either k: h/:c[k][0]]
h 
== #(
    #"T" 1
    #"i" 3
    #"p" 1
    #"s" 1
    #" " 4
    #"f" 2
    #"o...

We can achieve the same result with any - it evaluates and returns the first truthy value. The first value in the block is the value we want to test for existence, the seconf one - the default value. So, if the key doesn'r exist, any will return the default value:

h: #()
s: "Tips for golfing in Red"
foreach c s[h/:c: 1 + any[h/:c 0]]
h
== #(
    #"T" 1
    #"i" 3
    #"p" 1
    #"s" 1
    #" " 4
    #"f" 2
    #"o...
\$\endgroup\$
2
\$\begingroup\$

Use alter instead of append/insert

Sometimes it is certain that the value we are appending to a list, is not aleady present. In such case we can use alter instead of append for 1 byte less. alter appends a value if it's not found in the series, otherwise, removes it from the series.

>> b: [1 2 3 4 5]
== [1 2 3 4 5]
>> alter b 6
== true
>> b
== [1 2 3 4 5 6]
\$\endgroup\$
2
\$\begingroup\$

forall instead of foreach

If you reference the current value of the series only once, you can save a byte by using forall instead of foreach :

>> p: [3 1 4 1 5 9 2]
== [3 1 4 1 5 9 2]
>> foreach n p[prin n]
3141592
>> forall p[prin p/1]
3141592
\$\endgroup\$
1
\$\begingroup\$

Use some of the predefined char! values

>> ? char!
    null             #"^@"
    newline          #"^/"
    slash            #"/"
    escape           #"^["
    comma            #","
    lf               #"^/"
    cr               #"^M"
    space            #" "
    tab              #"^-"
    dot              #"."
    sp               #" "
    dbl-quote        #"^""

sp for space (#" " or " "), lf for newline (#"^/" or "^/"), tab (#"^-" or "^-") etc. can save you a byte if enclosed in brackets or between strings:

either a/1 = i[1][" "]
either a/1 = i[1][sp]
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.