Rebol calc-workdays not working on Red - rebol

I trying to execute this script https://www.mail-archive.com/rebol-bounce#rebol.com/msg01222.html in Red but I can't see why I got
calc-workdays now/date 3-feb-2007 [1-feb-2007]
*** Script Error: > operator is missing an argument
*** Where: do
*** Stack: print calc-workdays
found?: func [
"Returns TRUE if value is not NONE."
value
][
not none? :value
]
calc-workdays: func [
"Return number of workdays between two dates, excluding holidays"
date1 [date!] "Start date"
date2 [date!] "End date"
holidays [block!] "Block of dates to exclude (holidays, etc.)"
/non "Return number of non-work days (weekend + holidays) between 2 dates"
/local days day1 day2 diff param
][
days: copy []
set [day1 day2] sort reduce [date1 date2]
diff: day2 - day1
param: pick [[> 5 union][< 6 exclude]] either found? non [1][2]
loop diff [
day1: day1 + 1
if do param/1 day1/weekday param/2 [append days day1]
]
return length? do param/3 days holidays
]

Rebol2 seems to allow you to pass a WORD! to DO, and evaluate it. If the word happens to be a variable holding an ANY-FUNCTION!, it will be run...and if it's an infix "OP!" then it will be run as if it were not infix.
>> do quote > 1 2
== false
>> do quote < 1 2
== true
Red sort of does this also, but DO is not variadic. It can run 0 arity functions only:
>> foo: does [print "hi"]
>> do quote foo
hi
>> bar: func [x] [print x]
>> do quote bar "hi"
*** Script Error: bar is missing its x argument
The script in question was attempting to use this feature. But it could be done with ordinary COMPOSE or REDUCE. So change:
if do param/1 day1/weekday param/2 [append days day1]
To:
if do reduce [day1/weekday param/1 param/2] [append days day1]
That will build a block of code where the > or < operator is in the middle, and execute it normally without relying on this WORD!-dispatch or infix-dropping behavior of DO.
Similarly, change:
return length? do param/3 days holidays
To:
return length? do reduce [param/3 days holidays]
With those changes, and removing the found? (it's not necessary) then it appears to work.

Related

Value of local variables in a function seems not be released post function calling in Red/Rebol language

I construct a function named find-all to find all indexes of a given item in a series by "recursive".
The first calling of find-all gives the right output. However from the second calling, all outputs are appended together.
find-all: function [series found][
result: []
either any [empty? series none? s-found: find series found]
[result]
[append result index? s-found
find-all next s-found found]
]
;; test:
probe find-all "abcbd" "b" ;; output [2 4] as expected
probe find-all [1 2 3 2 1] 2 ;; output [2 4 2 4]
Since variables inside a function created with function are local, why does the value of variable result is still there during later funtion callings, which cause the result of the sencond calling of find-all does not begin with []?
And what is the correct recursive way to achieve this funciton?
The answer is evident if you inspect find-all after making these two calls:
>> ?? find-all
find-all: func [series found /local result s-found][
result: [2 4 2 4]
either any [empty? series none? s-found: find series found]
[result]
[append result index? s-found
find-all next s-found found
]
]
result is an indirect value, and its data buffer is stored on a heap. The data gets preserved between the calls and accumulated, because you do not re-create it with copy — result being local to function's context is unrelated to that.
Thanks to #9214's help, especially the description about indirect value. I give a solution like this:
find-all: function [series found][
either any [empty? series none? s-found: find series found]
[[]]
[append
reduce [index? s-found]
find-all next s-found found
]
]
;; test:
probe find-all "abcbd" "b" ;; output [2 4] as expected
probe find-all [1 2 3 2 1] 2 ;; output [2 4] as expected

How to get a formatted date and time string from `now`?

I'm using "Red Programming Language" version "0.6.4" on Windows and making a command line application.
I don't know much Red language and I don't understand many things. I did go over "work in progress" docs at (https://doc.red-lang.org/en/) before asking here.
I need to get a date and time string formatted as yyyymmdd_hhmm.
I've started with code like this:
Red []
dt: to string! now/year
print dt
which gives me 2019 but I need the other things month, day and time to obtain something like 20190608_2146
I tried also:
Red []
dt: to string! now/precise
print dt
which gives me 8-Jun-2019/21:47:51.299-07:00 but again what I needed was 20190608_2147
Question:
How to modify the code above to obtain something like 20190608_2147 from now?
Thank you.
I have written a script for Rebol and Red called 'Form Date' that will format dates/times in a similar fashion to STRFTIME. The Red version is here.
do %form-date.red
probe form-date now "%Y%m%d_%H%M"
print first spec-of :form-date
Within the script are individual snippets of code used for formatting the various components of a date! value.
You don't need the script for your specific example though, you can extract and join the various components thus:
date: now
rejoin [ ; reduce-join
form date/year
pad/left/with date/month 2 #"0"
pad/left/with date/day 2 #"0"
"_"
pad/left/with date/hour 2 #"0"
pad/left/with date/minute 2 #"0"
]
As the above solution has some problems under Rebol2 here a variation that works with Rebol and Red the same way
date: now
rejoin [
date/year
next form 100 + date/month
next form 100 + date/day
"_"
next form 100 + date/time/hour
next form 100 + date/time/minute
]
Here is another way:
rejoin [
now/year
either 10 > x: (now/month) [join "0" x][x]
either 10 > x: (now/day) [join "0" x][x]
"_"
either 10 > x: first (now/time) [join "0" x][x]
either 10 > x: second (now/time) [join "0" x][x]
]
Red has pad, so rgchris' answer up there is good. Yet, there is no need for date: now as rgchris has done:
rejoin [
now/year
pad/left/with now/month 2 #"0"
pad/left/with now/day 2 #"0"
"_"
pad/left/with first (now/time) 2 #"0"
pad/left/with second (now/time) 2 #"0"
]

Most elegant way to extract block by skipping every 2 element

Let's say I have
block: [a 1 b 2 c 3]
I want
[1 2 3]
Something like this is clunky and it doesn't work because I use word type (I'd like to have it word with word not string):
block: [a 1 b 2 c 3]
block2: []
counter: -1
foreach 'element block [
counter: negate counter
if counter append block2 element
]
The EXTRACT function should fit the bill here:
>> extract/index [a 1 b 2 c 3] 2 2
== [1 2 3]
It's fairly versatile for this type of thing.
>> help extract
USAGE:
EXTRACT series width
DESCRIPTION:
Extracts a value from a series at regular intervals.
EXTRACT is a function! value.
ARGUMENTS:
series [series!]
width [integer!] "Size of each entry (the skip)".
REFINEMENTS:
/index => Extract from an offset position.
pos [integer!] "The position".
/into => Provide an output series instead of creating a new one.
output [series!] "Output series".

TCL:: How to print numbers from 10 to 1 using while loop in TCL (How to decrement the variiable value)

TCL Script:
set a 10
while {$a < 1} {
puts $a
incr a
}
Expected output:
10
9
8
7
6
5
4
3
2
1
I am trying to print numbers from 10 to 1. But its not working (It prints nothing).
Is there a way to "decrement" the variable value (decr a)?
Thanks,
Kumar
Change the condition to $a > 1 and to decrement the value, you have to use incr a -1. Here we gave the step as -1.
same can be done by for loop also
for {set i 10} {$i > 1} {incr i -1} {
puts $i
}
I think your loop body is never executed because the condition yields false the very first time. You probably wanted to write ">" instead of "<".

Invalid Input Exception Handling - SmallTalk

Let a smalltalk msg named "sum" return the sum of elements in an array.
Eg: #(1 2 3 4 5) sum ----> 15
When the input is #(1 2 'a' 3 5) sum. The execution terminates and shows a big exception box.
Instead of that how can we gracefully exit the execution by just showing a message. I don't want the big exception window to be shown.
sum
|sum|
sum := 0
self do: [:a | sum := sum + a]
^sum
I tried to handle the exception the below way. However, I notice that the execution doesn't terminate in case of invalid input.
sum
|sum|
sum := 0
self do: [:a |
(a isInteger) ifFalse:[
^[Error signal] on: Exception
do: [:ex | Transcript show: 'Entered values are non-numeric. Hence comparison is not possible.']
]
sum := sum + a
]
^sum
If the below code is placed in the workspace, I expected the execution to be terminated at line 2. However, line 3 is also getting executed.
|temp|
temp := #(1 2 3 'as' 4 5) sum.
temp := temp*5.
Changing the sum method to ignore the wrong types in the input Array does not make sense. Furthermore by replacing it with a UI message you completely loose control over what kind of input is acceptable. Rather deal with these exception at the place you use sum:
[ ^ self readInput sum ]
on: Error do: [ :error| Transcript show: 'Invalid input provided for sum' ].