Cascade in Rebol - metaprogramming

In Logo Language, cascade is a procedure to to compose a function with itself several times (it is almost like fold in functional language).
Example:
add 4 add 4 add 4 5 --> cascade 3 [add 4 ?1] 5 == 17
2^8 --> cascade 8 [?1 * 2] 1
fibonacci 5 --> (cascade 5 [?1 + ?2] 1 [?1] 0)
factorial 5 --> (cascade 5 [?1 * ?2] 1 [?2 + 1] 1)
General notation for multi-input cascade, in Logo:
(cascade how many function1 start1 function2 start2 ...) with:
function1 -> ?1 ,
function2 -> ?2 ...
Cascade returns the final value of ?1.
In Rebol:
cascade1: func [howmany function1 start1] [....]
cascade2: func [howmany function1 start1 function2 start2] [....]
How to write cascade1 and cascade2 in Rebol ?

My answer uses REBOL 3, but could be backported to 2 without too much trouble. (I'd have done it in REBOL 2, but I don't have REBOL 2 on my system and haven't used it in a long time.) This implements cascade fully (i.e., with any number of "functions") and does it in an idiomatically REBOL kind of way: It uses a simple DSL.
cascade: funct [
count [integer!]
template [block!]
/only "Don't reduce TEMPLATE"
/local arg fun-block
][
param-list: copy []
param-number: 1
arg-list: copy []
fun-list: copy []
template-rules: [
some [
copy fun-block block! (
append param-list to word! rejoin ["?" ++ param-number]
append fun-list fun-block
)
copy arg any-type! (
append arg-list :arg
)
]
end
]
unless only [template: reduce template]
unless parse template template-rules [
do make error! rejoin ["The template " mold/flat template " contained invalid syntax."]
]
while [! tail? fun-list] [
fun-list: change fun-list func param-list first fun-list
]
fun-list: head fun-list
loop count [
temp-args: copy []
for f 1 length? fun-list 1 [
append/only temp-args apply pick fun-list f arg-list
]
arg-list: copy temp-args
]
first arg-list
]
Using it is simple:
print cascade 23 [[?1 + ?2] 1 [?1] 0]
This correctly gives the value 46368 from one of the cascade examples given in the Logo cascade documentation linked by the questioner. The syntax of the DSL should be brutally obvious. It's a series of blocks followed by starting arguments. The outer block is reduced unless the /only refinement is used. A block itself will work just fine as an argument, e.g.,
cascade 5 [[?1] [1 2 3]]
This is because the first block is interpreted as a "function", the second as a starting argument, the third as a "function" and so on until the template block is exhausted.
As far as I can tell, this is a complete (and rather elegant) implementation of cascade. Man, I love REBOL. What a shame this language didn't take off.

With bind, that Binds words to a specified context (in this case local context of function), and compose function, I get:
cascade: func [
times
template
start
] [
use [?1] [
?1: start
template: compose [?1: (template)]
loop times bind template '?1
?1
]
]
cascade 8 [?1 * 2] 1
== 256
cascade 3 [add 4 ?1] 5
== 17
val: 4
cascade 3 [add val ?1] 5
== 17
cascade2: func [
times
template1 start1
template2 start2
/local **temp**
] [
use [?1 ?2] [ ; to bind only ?1 and ?2 and to avoid variable capture
?1: start1
?2: start2
loop
times
bind
compose [**temp**: (template1) ?2: (template2) ?1: **temp**]
'?1
?1
]
]
cascade2 5 [?1 * ?2] 1 [?2 + 1] 1
== 120
cascade2 5 [?1 + ?2] 1 [?1] 0
== 8

Here is a somewhat working cascade in Rebol. It won't work with op! datatype--i.e. +, *--but it will work with add and multiply. You may want to check out the higher order functions script to see some other examples. I haven't had time to write cascade2 yet
cascade: func [
times [integer!]
f [any-function!]
partial-args [series!]
last-arg
][
expression: copy reduce [last-arg]
repeat n times [
insert head expression partial-args
insert head expression get 'f
]
expression
]
With your examples:
probe cascade 3 :add [4] 5
print cascade 3 :add [4] 5
will result in:
[make action! [[
"Returns the addition of two values."
value1 [scalar! date!]
value2
]] 4 make action! [[
"Returns the addition of two values."
value1 [scalar! date!]
value2
]] 4 make action! [[
"Returns the addition of two values."
value1 [scalar! date!]
value2
]] 4 5]
17
and
probe cascade 8 :multiply [2] 1
print cascade 8 :multiply [2] 1
Will result in:
[make action! [[
"Returns the first value multiplied by the second."
value1 [scalar!]
value2 [scalar!]
]] 2 make action! [[
"Returns the first value multiplied by the second."
value1 [scalar!]
value2 [scalar!]
]] 2 make action! [[
"Returns the first value multiplied by the second."
value1 [scalar!]
value2 [scalar!]
]] 2 make action! [[
"Returns the first value multiplied by the second."
value1 [scalar!]
value2 [scalar!]
]] 2 make action! [[
"Returns the first value multiplied by the second."
value1 [scalar!]
value2 [scalar!]
]] 2 make action! [[
"Returns the first value multiplied by the second."
value1 [scalar!]
value2 [scalar!]
]] 2 make action! [[
"Returns the first value multiplied by the second."
value1 [scalar!]
value2 [scalar!]
]] 2 make action! [[
"Returns the first value multiplied by the second."
value1 [scalar!]
value2 [scalar!]
]] 2 1]
256

Related

Compare round half up numbers

Compare the round half up values of num1=172 and num2=172.8, where num2's value will be 173.
Print whether or not num1 and num2 are equal.
if (( $(echo "$num1 == $num2" | bc -l) )); then
echo "num1 and num2 are equal"
else
echo "number are not close to each other'
fi
Use https://en.wikipedia.org/wiki/Dynamic_programming . Your problem consist of:
rounding numbers to zero decimal digits
comparing the results
The first part can be found on stackoverflow, like Round a divided number in Bash , the second part can be done with just == even with string comparison.
round() {
printf "%.${2:-0}f" "$1"
}
num1=172
num2=172.8
if (( $(round "$num1") == $(round "$num2") )); then
echo "Equal"
else
echo "Not equal"
fi
The (( arithmetic expression is specific to Bash shell.
You can compare them in bc using the same method. First take a rounding function from
https://github.com/zg/bc/blob/master/code/funcs.bc and then compare the rounded numbers:
if (($(bc -l <<EOF
define int(x) { auto os;os=scale;scale=0;x/=1;scale=os;return(x) }
int($num1) == int($num2)
EOF
) )); then
No bash, no external utils, just pure (ugly) POSIX shell code in two functions:
rhup ()
{
[ "${1##*.[5-9]*}" ]
echo "$((${1%%.*}+$?))"
}
req ()
{
a=
[ "$(rhup "$1")" = "$(rhup "$2")" ] || a="not "
echo "When rounded half up $1 and $2 are ${a}equal."
}
Demo:
req 2 3 ; req 2 2.2 ; req 2.4 2.5
Output:
When rounded half up 2 and 3 are not equal.
When rounded half up 2 and 2.2 are equal.
When rounded half up 2.4 and 2.5 are not equal.
How it works:
Given a number rhup (short for round half up) uses shell parameter substitution to check if a half-up decimal suffix exists. Then it adds the resulting error code of 0 or 1 to the number's integer prefix and prints the sum.
req (short for rhup equal) runs rhup on two numbers, compares them, if they're not equal, sets $a to "not ", then prints the desired conditional English sentence.
Neither function does any error checking on the input values.

J to print out status bar and percentage

I am calculating the processing time by putting
start =: 6!:0 ''
at the top of my code and putting
end =: (6!:0 '') - start
at the end of my code. However, is there any way that I can implement a status bar like this in J?
[==== ] 25%
[============ ] 50%
[==========================] 100%
Thank you!
If I understand your question, you could use smoutput defined as 0 0 $ 1!:2&2 by the system to display your processing milestones on the screen
someverb =: 3 : 0
smoutput '{ }'
code
smoutput '{+++ }'
more code
smoutput '{+++++ }'
more code
smoutput '{++++++++}'
)
but you would have to know that the places that you insert the smoutput expressions would correspond to the amount of processing that had taken place.
As an example:
test =: 3 : 0
​smoutput 6!:0 'hh:mm:ss.sss'
​6!:3 (2) NB. 2 second delay
​smoutput 6!:0 'hh:mm:ss.sss'
​6!:3 (2) NB. 2 second delay
​smoutput 6!:0 'hh:mm:ss.sss'
​)
test ''
14:53:42.313
14:53:44.317 NB. after two second delay
14:53:46.326 NB. after two second delay
or closer to the output you would like
test1 =: 3 : 0
start=. 6!:0 ''
smoutput '[ ] 0%'
6!:3 (2) NB. 2 second delay
smoutput '[=== ] 25%'
6!:3 (2) NB. 2 second delay
smoutput '[====== ] 50%'
6!:3 (4) NB. 4 second delay
smoutput '[============] 100%'
(6!:0 '')- start
)
test1 ''
[ ] 0%
[=== ] 25%
[====== ] 50%
[============] 100%
0 0 0 0 0 8.01821

calling function in shell script

the user will enter 3 numbers , and I want to calculate these number's roman values. I copy paste to_roman() function but its take 3 arguments . I'm confused . then I call this function in case statement.How can I implement this function for 3 numbers ? any implementation advice ?
to_roman () # Must declare function before first call to it.
{
number=$1
factor=$2
rchar=$3
let "remainder = number - factor"
while [ "$remainder" -ge 0 ]
do
echo -n $rchar
#echo $remainder
let "number -= factor"
let "remainder = number - factor"
done
return $number
}
to_roman $num 10 x
num=$?
to_roman $num 9 ix
num=$?
to_roman $num 5 v
num=$?
to_roman $num 4 iv
num=$?
to_roman $num 1 i
You just read the params in function like $1 $2 $3 and so on.
$# being the total number of parameters
Depending on your implementation you may want to loop over the arguments for n argument support per #EitanReiner suggestion.
It is done with $#. For example:
for param in "$#"
do
echo "$param"
done
In your case it probably would be an overkill since you get 3 parameters only. Anyway, you should check if you got exactly 3 parameters.

Require efficient and probably one to two liner solution to replace space

Having following bash scripts to find index of the command from command list
#!/bin/bash
cmdlist="cmd1,cmd2,cmd,cmd24,cmd25,cmd4,cmd10,cmd9,cmd000001,cmdxyz"
cmdlist="${cmdlist//,/ }" #To Replace , with space in array list
cmdlist="${cmdlist//cmd/ }" #To get index of command
echo $cmdlist //Added for clarification
for a in $cmdlist
do
if [[ $a == *[^[:digit:]]* ]] || [[ $a -gt 50 ]]
then
printf "invalid command index : %s\n" $a
else
printf "command index : %s\n" $a
fi
done
As you can see how I extract command index from command list but it's failing (of course it fails) in some condition. So want make some validations as follows:
1) In list, if argument is cmd then it will skipped and not replace space instead cmd string because argument length must be greater then 3.
2) In list, if argument is cmd0001 then also skipped and not replace space instead cmd string because argument length must be less or equal 5 and greater then 3.
Following above validation I achieved by taking for..loop, taking temporary array then compare each argument and validate then store in temporary array and finally copy temporary array in original one.So this is too long procedure.
Any one have idea for batter solution?
Like
cmdlist="${cmdlist//cmd/ }" #To get index of command
command only replace space instead cmd in target argument if condition [[ length -gt 3 ]] && [[ length -le 5 ]] match.
Note: have already have solution using for..loop.
UPDATE: Added more detail for what i want
I got output this from script
command index : 1
command index : 2
command index : 24
command index : 25
command index : 4
command index : 10
command index : 9
command index : 000001
invalid command index : xyz
but i want this
command index : 1
command index : 2
invalid command index : cmd
command index : 24
command index : 25
command index : 4
command index : 10
command index : 9
command index : cmd000001
invalid command index : cmdxyz
So basically leave the argument which not in validation range and mark as invalid index(nothing to do not require to replace space in place of cmd string.)
More UPDATE: Again added more detail to clarify exactly what i want
Have added one echo statement before for..loop in my script ( see modified above script ) which give me output like this
1 2 24 25 4 10 9 000001 xyz
but i want
1 2 cmd 24 25 4 10 9 cmd000001 cmdxyz
means leave argument as it is if it violate validation like in my list third argument is cmd.It violate the condition becasue it's length not greater then 3.Now see last two argument in list cmd000001,cmdxyz It violate the condition because it's length greater then 5. Valid argument is one for which length must be greater then 3 && less or equal 5.
Hope this will clarify what i want.
Editing as per your update:
Get the values in an array and check within the loop if those meet the required criteria:
cmdlist="cmd1,cmd2,cmd,cmd24,cmd25,cmd4,cmd10,cmd9,cmd000001,cmdxyz"
IFS=, read -a arr <<< "$cmdlist"
for a in "${arr[#]}"
do
v="${a/cmd/}"
if ((v > 50)) || ((v <= 0))
then
printf "invalid command index : %s\n" $a
else
printf "command index : %s\n" $v
fi
done
For your input, it'd produce:
command index : 1
command index : 2
invalid command index : cmd
command index : 24
command index : 25
command index : 4
command index : 10
command index : 9
command index : 000001
invalid command index : cmdxyz
Old answer:
Instead of attempting to replace , with spaces and so on, read the string delimited by comma into an array. Manipulate the array to get one containing the desired strings.
$ IFS=, read -a arr <<< "$cmdlist"
$ foo=(${arr[#]/cmd/})
$ for i in "${foo[#]}"; do echo $i; done
1
2
24
25
4
10
9
000001
xyz
$
Checking...
for a in "${foo[#]}"
do
if [[ $a == *[^[:digit:]]* ]] || [[ $a -gt 50 ]]
then
printf "invalid command index : %s\n" $a
else
printf "command index : %s\n" $a
fi
done
produces:
command index : 1
command index : 2
command index : 24
command index : 25
command index : 4
command index : 10
command index : 9
command index : 000001
invalid command index : xyz
A note of caution: Numbers with leading zeros (as you have in your example) would be considered as octal and might produce unexpected results:
$ [[ 0024 -gt 22 ]] && echo greater || echo smaller
smaller

What does an ampersand in arithmetic evaluation and numbers with x's mean in Bash?

I'm curious about what exactly the following comparison does, in as much detail as possible, especially relating to the 0x2 and the & characters and what exactly they do,
if [ $((${nValid} & 0x1)) -eq 1 ]; then
#...snip...
fi
if [ $((${nValid} & 0x2)) -eq 2 ]; then
#...snip...
fi
& is the bitwise AND operator. So you are asking to do a bitwise and between 0x1 and the value that ${nVAlid} is returning.
For more information on bitwise operations look here.
A shell script interprets a number as decimal (base 10), unless that number has a special prefix or notation. A number preceded by a 0 is octal (base 8). A number preceded by 0x is hexadecimal (base 16). A number with an embedded # evaluates as BASE#NUMBER (with range and notational restrictions).
So, in [ $((${nValid} & 0x1)) -eq 1 ], $nValid is anded with 0x1 and compared with decimal 1. Similarly the second comparison too.
Read this and this for detailed info.
It's testing nValid on a per-bit basis.
The bitwise AND operator (&) means that bit-by-bit, the operator will do an AND comparison. So, if nValid is a byte (8 bit) value, then look at the operation in binary:
nValue & 0b00000001
If nValue is 42, then the operation would look like this
(nValue = 0b00101010) & 0b00000001 => 0b00000000 // (does not have the last bit set)
(nValue & 0b00000001) == 0b00000001 // false
and for the 2nd (nValid & 0x2)
(nValue = 0b00101010) & 0b00000010 => 0b00000010 // (has the 2nd-to-last bit set)
(nValue & 0b00000010) == 0b00000010 // true
This is useful for testing flags within variables; usually you use the AND to check for flags by isolating bits and the OR to combine flags.
0b00001000 | 0b00000010 | 0b00000001 => 0b00001011
0x1 and 0x2 are the hexadecimal notations for 1 and 2. The & is the bitwise AND operator. What these lines do is test the value in nValid whether the least significant bit (0x1) and second least significant bit (0x2) are set.
The scheme goes like this (C notation):
if (val & (1 << bitNumber) == (1 << bitNumber)) {
// The bit at position bitNumber (from least to most significant digit) is set
}
The << is the left bitshift operator. 1 << 0 == 1, 1 << 1 == 2, 1 << 2 == 4, ...
So for better readability the lines should be more like:
if [ $((${nValid} & X)) -eq X ]; then
where X is a power of 2 (instead of mixing hexadecimal and decimal notation).
That could be rewritten as:
if (( nValid & 2#00000001 )); then
#...snip...
fi
if (( nValid & 2#00000010 )); then
#...snip...
fi
with the number of binary digits chosen to be most appropriate for the context. It's not necessary to test for equality if you're only checking one bit*. You could still use the hex representation if it makes more sense. The braces and dollar sign aren't necessary in this context.
You might want to use constants with meaningful names instead of hard-coded values:
declare -r FOO=$((2#00000001))
declare -r BAR=$((2#00000010))
if (( nValid & FOO )); then
#...snip...
fi
if (( nValid & BAR )); then
#...snip...
fi
* You will need to test for equality if you're testing multiple bits at the same time:
if (( (nValid & (FOO | BAR)) == (FOO | BAR) )); then
#...snip...
fi
You will need the extra parentheses since == has a higher precedence than the bitwise operators.
Clearing and setting bits in Bash:
(( var |= FOO )) # set the bits in FOO into var
(( var &= ~BAR )) # clear the bits in BAR from var

Resources