2024/10/30

Rexx

Rexx (Restructured Extended Executor) 是 Mike Cowlishaw 在 IBM 任職時, 於 1979 年 3 月 20 日到 1982 年中作為個人專案開發的程式語言,而後被 IBM 採用, 主要用於 IBM 的 Mainframe computer 上,而在其它大部份的平台也可以找到解釋器或編譯器。 Rexx 還有物件導向的版本,稱為 Object Rexx。

自由軟體的實作主要為符合 1996 年 ANSI 標準實作的 Regina Rexx Interpreter, 加入物件導向程式設計的 Open Object Rexx, 以及與 Java 整合的 NetRexx。 我在學習時使用的是 ooRexx。

Rexx 是一個不區分大小寫 (Case-insensitive) 的程式語言。

下面就是 Rexx 版的 Hello World 程式:

/* Main program */ 
say "Hello, World!"

就如同上面的例子所看到的,Rexx 的註解使用 /**/,中間的文字就是註解要寫的說明。

Variables

In Rexx, all variables are bound with the '=' statement. Variables in Rexx are typeless, and initially are evaluated as their names, in upper case. Thus a variable's type can vary with its use in the program:

say hello /* => HELLO */
hello = 25
say hello /* => 25 */
hello = "say 5 + 3"
say hello /* => say 5 + 3 */
interpret hello /* => 8 */
drop hello
say hello /* => HELLO */

Rexx has no direct support for arrays of variables addressed by a numerical index. Instead it provides compound variables. A compound variable consists of a stem followed by a tail. A . (dot) is used to join the stem to the tail. If the tails used are numeric, it is easy to produce the same effect as an array.

do i = 1 to 10
    stem.i = 10 - i
end

do i over stem.
    say i '-->' stem.i
end

ooRexx 提供了 do over 的方式可以用來迭代 stem 內的值(注意:這不是 REXX 標準所規範的語言特性)。

Control Structures

對於迴圈而言,Rexx 提供了 do loop, do while loop 與 do until loop 等方式。

do while [condition]
  [instructions]
end
do until [condition]
  [instructions]
end

Like most languages, Rexx can loop while incrementing an index variable and stop when a limit is reached:

do index = start [to limit] [by increment] [for count]
  [instructions]
end

Rexx permits counted loops, where an expression is computed at the start of the loop and the instructions within the loop are executed that many times:

do expression
  [instructions]
end

Rexx can even loop until the program is terminated:

do forever
  [instructions]
end

Like PL/I, Rexx allows both conditional and repetitive elements to be combined in the same loop:

do index = start [to limit] [by increment] [for count] [while condition]
  [instructions]
end
do expression [until condition]
  [instructions]
end

下面是一個迴圈的例子:

/* Main program */ 
do i = 1 to 9 by 1
    do j = 1 to 9 by 1
        say i 'x' j '=' i*j
    end
end

對於條件判斷來說,Rexx 提供了 ifselect 等方式。

if [condition] then do
  [instructions]
  end
else do
  [instructions]
end

For single instructions, DO and END can also be omitted:

if [condition] then
  [instruction]
else
  [instruction]

SELECT is Rexx's CASE structure.

select
  when [condition] then
  [instruction] or NOP
  when [condition] then
  do
  [instructions] or NOP
  end
  otherwise
  [instructions] or NOP
end

The NOP instruction performs "no operation", and is used when the programmer wishes to do nothing in a place where one or more instructions would be required.

下面是使用 select 的例子:

/* Main program */
say "Enter your temperature in degrees Celsius:"
pull degrees

select
    when DataType(degrees) \= "NUM" then
        say "That's not a number. I can't help you."
    when degrees < 36.5 then
        say "Your body temperature is a bit low."
    when degrees > 37.5 then
        say "I think you have a fever."
    otherwise
        say "You're temperature seems normal."
end

Write a program that displays the digits from 1 to n then back down to 1; for instance, if n = 5, the program should display 123454321. You are permitted to use only a single for loop. The range is 0 < n < 10.

/* Main program */
parse arg n

if DataType(n) \= "NUM" then do
    say "That's not a number."
    exit
end


if n < 1 | n > 9 then do
    say "Out of range."
    exit
end

select
    when n == 1 then
        say "1"

    when n == 2 then
        say "121"

    when n == 3 then
        say "12321"

    when n == 4 then
        say "1234321"

    when n == 5 then
        say "123454321"

    when n == 6 then
        say "12345654321"

    when n == 7 then
        say "1234567654321"

    when n == 8 then
        say "123456787654321"

    when n == 9 then
        say "12345678987654321"

    otherwise
        say "Please input 0 < n < 10"
end

exit

使用迴圈的解法:

/* Main program */
parse arg n

if DataType(n) \= "NUM" then do
    say "That's not a number."
    exit
end


if n < 1 | n > 9 then do
    say "Out of range."
    exit
end

positive = 1
count = 0
do forever
    if positive == 1 then do
        count = count + 1
        call charout , count
        if count == n then do
            positive = 0
            iterate
            end
        end
    else do
        count = count - 1
        if count > 0 then
            call charout , count
        else
            leave    
    end
end
say

exit

下面是一個簡單的猜測數字遊戲:

/* A guess number game */
the_number = random(1, 1000)

do forever
    call charout , 'Please input a number: '
    pull the_guess

    if the_number = the_guess then do
        say 'You guesses it!'
        leave
        end
    else if the_number > the_guess then do
        say 'Please guess more higher'
        iterate
        end
    else do
        say 'Please guess more lower'
        iterate
    end
end

exit

Numbers

Rexx 一般而言預設處理的位數為 9(或者使用 digits() function 取得目前的設定)。 因此如果在程式使用的位數超過預設值,需要使用 numeric digits 設定。

numeric digits 12

Subroutines

Rexx 的程式可以分割為 function 或者是 subroutine,二者的差別在於 function 一定會傳回回傳值, 而 subroutine 沒有(或者說不一定有)回傳值。

下面是一個 function 的例子:

/* Main program */ 
say add(5,6) 
exit 

add: 
PARSE ARG a,b 
return a + b

下面是 recursive function 的例子:

/* Main program */ 
do n = 1 to 5 
    say 'The factorial of' n 'is:' factorial( n ) 
end 
return  

/* Function to get factorial */ 
factorial: procedure 
n = arg(1) 
if n = 1 then 
    return 1
return n * factorial( n - 1 )

我們可以使用 call statements 來呼叫 subroutine。如果呼叫之後有回傳值返回,可以使用 RESULT 變數取得其值。

/* Main program */
call subr
exit 0

subr:
say 'You are inside internal subroutine.'
return

如果要取得參數的數目,可以使用 arg() 取得。

/* Main program */ 
call add 1, 2 
exit

add: 
PARSE ARG a,b 
say 'Arg number:' arg() 
c = a + b 
say 'Result:' c
return

File IO

下面是讀取檔案的例子,一行一行的從 /etc/os-release 讀出資料,然後判斷目前 Linux distribution 的名稱。

/* Main program */
filename = '/etc/os-release'
do while lines(filename) > 0  
    line_str = linein(filename)
    parse var line_str key "=" value
    
    if compare(key, 'NAME') == 0 then do
        say value
        leave
    end
end

System Commands

REXX 支援執行外部程式的能力,下面是一個例子:

/* Main program */ 
'ls'
if rc == 0 then 
    say 'The command executed successfully' 
else 
    say 'The command failed, The error code is =' rc

再來是另外一個例子,1-9位數不重複印出來的練習問題,
使用者輸入1 印1-9
使用者輸入2 印1-98 (11, 22, 33等重複的不印)
使用者輸入3 印1-987 (121, 988, 667等有重複的不印):

call charout , 'Please input a number: '
pull num

if num < 1 | num > 9 then do
    say "Out of range."
    exit
end

'seq '10**num - 1 '| egrep -v "([0-9]).*\1"'

To send a command to a specific environment, use this format of the address instruction:

address environment expression

下面就是一個使用的例子:

address system 'ls'

address 也可以作為輸入與輸出的重新導向的設定(input, output 與 error),下面是一個例子:

/* Main program */ 
address system 'ls' with output stem files.

do i = 1 to files.0
    say files.i
end

在上面的例子中,輸出的結果將會放到 files. 這個 compound variable 中, 而 files.0 會記錄結果的數目。

下面則是從 /etc/os-release 檔案取得內容,然後輸出到 compound variable 的例子。

/* Main program */ 
address system 'cat' with input stream '/etc/os-release' output stem files.
do i = 1 to files.0
    say files.i
end

參考資料

沒有留言:

張貼留言

注意:只有此網誌的成員可以留言。