只在此山中,雲深不知處


聽首歌



© 2018 by Shawn Huang
Last Updated: 2018.5.27

Ring


下載與安裝

首先到官網(https://ring-lang.github.io/)首頁,點擊Download,出現如下畫面: 選擇適合你的作業環境的檔案下載。例如使用Windows就下載Ring 1.14 For Windows (32bit and 64bit)。執行下載後的檔案,選擇你想要安裝的路徑(e.g. c:\ring),然後進行安裝。經過一番枯燥的檔案萃取,終於安裝完成,進入到該資料夾,找到RingNotepad.exe,雙擊執行。 之後出現 這即是我們要使用的IDE。

基本操作

先熟悉一下。首先建立一個資料夾(e.g. C:\ring\codes)來儲存程式碼檔案,建立一個延伸檔名為ring的檔案(e.g. Hello.ring,也可以直接save as原來的noname檔案)。不免俗地來顯示一下Hello World!,輸入以下指令: 你可以在下拉選單中選擇Program>Run GUI Application(No Console),或是點擊快捷鍵,或是按Ctrl+F5。 這裡可以先學會幾件事, 若是需要有輸入數值呢?使用give關鍵字,例如:
  1. ? "What's your name?"
  2. give yourname
  3. ? "Hello " + yourname
  4. put "How old are you?" + nl
  5. get age
  6. put "You are " + age + " years old." + nl
  7. Load "stdlib.ring"
  8. print("What is your favorite color?"+nl)
  9. fcolor = GetString()
  10. print("#{fcolor} is your favorite color")
在底下的Input輸入欄中輸入然後按send。

變數與資料型態

Ring的基本變數型態為numbers, strings, objects, and lists。宣告時無須指定變數型態。
numbers:包含整數、實數。
  1. a = 1
  2. b = 3.14
  3. c = -10
  4. d = 100/7
  5. e = 0
  6. see "a = " + a + nl + "b = " + b + NL + "c = " + c + nL + "d = " + d + Nl + "e = " + e
a~e都是數字。
strings:使用雙引號包括的為字串。
  1. s1 = "String" // 字串
  2. s2 = time() # 目前時間
  3. s3 = date() // 目前日期
  4. s4 = "" # 空字串
  5. ? s1 + NL + s2 + NL + s3 + NL + s4

objects:之後再提。
lists:先簡單介紹。
  1. alist = [1,2,3]
  2. blist = "a":"f" # 產生a到f的list
  3. ? alist + blist
  4. ? alist[4]
  5. ? Len(alist)

好像少了boolean,原則上初始值true=1,false=0。
  1. ? true
  2. see False

在Ring中,null的涵義如下:
  1. s = ""
  2. s1 = "String"
  3. n = "null"
  4. ? isNULL(s)
  5. ? isNULL(s1)
  6. ? isNULL(n)
  7. ? isNull(null) # null為變數,內定值為null
  8. ? s1 + null

使用以下函數來確認變數型態: type()、isString()、isNumber、isList()、isNull()。
  1. n1 = 10
  2. n2 = 3.1416
  3. n3 = 0
  4. n4 = 100/7
  5. s1 = "String"
  6. s2 = "null"
  7. List1 = 1:10
  8. see type(n1)+NL+type(s1)+NL+type(s2)+NL+type(List1)+NL
  9. put isString(n4)+NL+isString(s1)+NL+isString(s2)+NL
  10. ? isList(List1)+NL+isList(n2)
  11. ? isNull(n3)+NL+isNull(s2)
此外還可以使用官網中(https://ring-lang.sourceforge.io/doc1.10/checkandconvert.html)羅列的函數來確認資料內容,可以自己練習看看,之後若有機會用到再介紹。
若欲將某變數型態改變為另一型態,使用以下函數:number()、string()、ascii()、char()、hex()、dec()、str2hex()、hex2str()。
  1. a = "20"
  2. a = number(a)
  3. ? type(a)
  4. a = string(a)
  5. ? type(a)
  6. b = "A"
  7. ? ascii(b) // ascii碼
  8. ? char(109) // ascii碼所代表的符號
  9. ? hex(20) // 十進位數字轉為16進位數字
  10. ? dec("14") // 16進位數字(字串顯示)轉為十進位數字
  11. ? str2hex("String") // 537472696e67
  12. ? hex2str("537472696e67") // String
命名
命名的慣例在各個語言中大多類似,在此不再贅述。

運算子

Ring的運算子與其他許多語言多有類似,以下僅做簡單解釋:
運算優先順序
Operator
.[](){}
- ~ :Literal [list items]
++ --
Start:End
* / %
+ -
<< >>
&
| ^
< > <= >=
= !=
not !
and or && ||
Assignment = += -= *= /= %= >>= <<= &= ^= |=
?
永遠記得控制執行先後的好幫手:小括號()。

Control Flow

if statement
Ring的條件式,可以有如下寫法:
I: if...but...else...ok
  1. ? "你最喜歡的季節?
  2. 1. 春
  3. 2. 夏
  4. 3. 秋
  5. 4. 冬
  6. "
  7. give season
  8. if season = 1
  9. see "你最喜歡的季節是春天"
  10. but season = 2
  11. see "你最喜歡的季節是夏天"
  12. but season = 3
  13. see "你最喜歡的季節是秋天"
  14. else
  15. see "你最喜歡的季節是冬天"
  16. ok

II: if...elseif...else...ok
  1. ? "你最喜歡的季節?
  2. 1. 春
  3. 2. 夏
  4. 3. 秋
  5. 4. 冬
  6. "
  7. give season
  8. if season = 1
  9. see "你最喜歡的季節是春天"
  10. elseif season = 2
  11. see "你最喜歡的季節是夏天"
  12. elseif season = 3
  13. see "你最喜歡的季節是秋天"
  14. else
  15. see "你最喜歡的季節是冬天"
  16. ok

III: if {}
  1. ? "你最喜歡的季節?
  2. 1. 春
  3. 2. 夏
  4. 3. 秋
  5. 4. 冬
  6. "
  7. give season
  8. if season = 1 {
  9. see "你最喜歡的季節是春天"
  10. elseif season = 2
  11. see "你最喜歡的季節是夏天"
  12. elseif season = 3
  13. see "你最喜歡的季節是秋天"
  14. else
  15. see "你最喜歡的季節是冬天"
  16. }
現在來練習一下知名的BMI的例子:
  1. ? "請問你的體重是幾公斤?"
  2. get w
  3. ? "請問你的身高是幾公尺?"
  4. get h
  5. bmi = w/h/h
  6. ? ""
  7. if bmi <= 18.5
  8. see "太瘦"
  9. but bmi > 18.5 and bmi <= 23.9
  10. see "標準體重"
  11. elseif bmi > 23.9 and bmi < 27.9
  12. see "有點過重"
  13. else
  14. see "胖"
  15. ok
switch
switch跟if else作用類似,一樣有三種寫法:
I: switch...on...other...off
  1. ? "你最喜歡的季節?
  2. 1. 春
  3. 2. 夏
  4. 3. 秋
  5. 4. 冬
  6. "
  7. give season
  8. switch season
  9. on 1
  10. () see "你最喜歡的季節是春天"
  11. on 2
  12. see "你最喜歡的季節是夏天"
  13. on 3
  14. see "你最喜歡的季節是秋天"
  15. on 4
  16. see "你最喜歡的季節是冬天"
  17. other
  18. see "請重新選擇"
  19. off // 使用off來關閉switch

II: switch...case...else...end
  1. ? "你最喜歡的季節?
  2. 1. 春
  3. 2. 夏
  4. 3. 秋
  5. 4. 冬
  6. "
  7. give season
  8. switch season
  9. case 1
  10. () see "你最喜歡的季節是春天"
  11. case 2
  12. see "你最喜歡的季節是夏天"
  13. case 3
  14. see "你最喜歡的季節是秋天"
  15. case 4
  16. see "你最喜歡的季節是冬天"
  17. else // 可與other替換
  18. see "請重新選擇"
  19. end // 可與off替換

III: switch{}
  1. ? "你最喜歡的季節?
  2. 1. 春
  3. 2. 夏
  4. 3. 秋
  5. 4. 冬
  6. "
  7. give season
  8. switch season {
  9. case 1
  10. see "你最喜歡的季節是春天"
  11. on 2 // case與on效果相同
  12. see "你最喜歡的季節是夏天"
  13. case 3
  14. see "你最喜歡的季節是秋天"
  15. on 4
  16. see "你最喜歡的季節是冬天"
  17. else // 可與other替換
  18. see "請重新選擇"
  19. } // 有大括號則不使用off或end
while loop
Loop一樣分while loop與for loop,不囉嗦,直接看例子學語法:
I: while...end
  1. n=1
  2. while n<10
  3. see n+NL
  4. n++
  5. end # 使用end來作為while之結束
  6.  
  7. while True
  8. ? "guess big or small (quit to leave)"
  9. get bs
  10. if bs = "quit" see "See you next time." exit # 使用exit來離開while loop
  11. else
  12. computer = random(12)+1 # 產生1-13之隨機整數
  13. if bs = "big"
  14. if computer >= 7 see "You win>> "+computer+NL
  15. else see "You lose>> "+computer+NL
  16. ok
  17. elseif bs = "small"
  18. if computer <=7 see "You win>> "+computer+NL
  19. else see "You lose>> "+computer+NL
  20. ok
  21. else see "Unknown instruction."
  22. ok
  23. ok
  24. end

II: while{}
  1. n=1
  2. while n<10{
  3. see n+NL
  4. n++
  5. } // 使用{}則不使用end
  6.  
  7. while True {
  8. ? "guess big or small (quit to leave)"
  9. get bs
  10. if bs = "quit" see "See you next time." exit # 使用exit來離開while loop
  11. else
  12. computer = random(12)+1 # 產生1-13之隨機整數
  13. if bs = "big"
  14. if computer >= 7 see "You win>> "+computer+NL
  15. else see "You lose>> "+computer+NL
  16. ok
  17. elseif bs = "small"
  18. if computer <=7 see "You win>> "+computer+NL
  19. else see "You lose>> "+computer+NL
  20. ok
  21. else see "Unknown instruction."
  22. ok
  23. ok
  24. } // 使用{}則不使用end
好像只有兩種寫法。
for loop
試試看for loop
I: for...next
  1. for i=1 to 10
  2. put i+NL
  3. next # 用next表示往下一個
  4.  
  5. for i=1 to 10 step 2 # step表步幅,可以是負的(i由大到小)
  6. see i+NL
  7. next
  8.  
  9. lista = 1:10
  10. for i in lista # in 表示在list
  11. ? i
  12. next
  13.  
  14. for i in lista step 2
  15. ? i
  16. next
  17.  
  18. for i in lista
  19. if i%2=0 i="none" ok # 表示我們可以在for loop內修改lista內的值
  20. next
  21.  
  22. put lista

II: for...end
  1. for i=1 to 10
  2. put i+NL
  3. end # 用end替代next
  4.  
  5. for i=1 to 10 step 2 # step表步幅,可以是負的(i由大到小)
  6. see i+NL
  7. end
  8.  
  9. lista = 1:10
  10. for i in lista # in 表示在list
  11. ? i
  12. end
  13.  
  14. for i in lista step 2
  15. ? i
  16. end
  17.  
  18. for i in lista
  19. if i%2=0 i="none" ok # 表示我們可以在for loop內修改lista內的值
  20. end
  21.  
  22. put lista

III: for{}
  1. for i=1 to 10{
  2. put i+NL
  3. } # 用{}替代end與next
  4.  
  5. for i=1 to 10 step 2{ # step表步幅,可以是負的(i由大到小)
  6. see i+NL
  7. }
  8.  
  9. lista = 1:10
  10. for i in lista{ # in 表示在list
  11. ? i
  12. }
  13.  
  14. for i in lista step 2{
  15. ? i
  16. }
  17.  
  18. for i in lista{
  19. if i%2=0 i="none" ok # 表示我們可以在for loop內修改lista內的值
  20. }
  21.  
  22. put lista
exit
已知exit可以用來離開loop,尚可以使用exit 2來離開兩層nested的loop,例如:
  1. for i=1 to 10
  2. for j=1 to 10
  3. see "" + i + "*" + j + "=" + i*j + NL
  4. if i*j>50
  5. exit 2
  6. ok
  7. next
  8. end
do...again
原則上跟其他語言的do...while相同,先執行一次再判斷,可保證至少執行一次
  1. x = 1
  2. do
  3. ? x
  4. x++
  5. again x < 5
  6.  
  7. do # x=5 now,至少做一次
  8. ? x
  9. x--
  10. again x>5
loop
loop就是continue,直接看例子。
  1. for i in 1:10
  2. if i%2=0
  3. loop # 開始新的iteration
  4. else
  5. ?i
  6. ok
  7. end

函數(function)

函數應該不陌生,來看Ring的寫法:
I: func
  1. hello() # 在函數定義的前面呼叫
  2. circle(10)
  3. ? square(10,20)+square(5,6)
  4.  
  5. func hello
  6. see "Hi, there"+NL
  7.  
  8. func circle r
  9. see 3.1416*r*r+NL
  10.  
  11. func square l,w
  12. return l*w # 使用return傳回值

II: def...[end]
  1. hello() # 在函數定義的前面呼叫
  2. circle(10)
  3. ? square(10,20)+square(5,6)
  4.  
  5. def hello
  6. see "Hi, there"+NL
  7. end
  8.  
  9. def circle r
  10. see 3.1416*r*r+NL
  11. # end可有可無
  12.  
  13. def square l,w
  14. return l*w # 使用return傳回值
  15. end

III:func[{}]
  1. hello() # 在函數定義的前面呼叫
  2. circle(10)
  3. ? square(10,20)+square(5,6)
  4.  
  5. func hello{
  6. see "Hi, there"+NL
  7. } # 很顯然{}可有可無
  8.  
  9. def circle r{
  10. see 3.1416*r*r+NL
  11. }
  12.  
  13. func square l,w{
  14. return l*w # 使用return傳回值
  15. }
main function
可有可無的函數,應該是保留C語言的習慣,可用作收納整理程式碼
  1. func hello{
  2. see "Hi, there"+NL
  3. } # 很顯然{}可有可無
  4.  
  5. def circle r{
  6. see 3.1416*r*r+NL
  7. }
  8.  
  9. func square l,w{
  10. return l*w # 使用return傳回值
  11. }
  12.  
  13. func main
  14. put "先執行函數定義之前之程式指令,然後再執行此處之指令"+NL
  15. see "若函數定義之前無程式指令,則由main開始執行,此函數可有可無"+NL
  16. hello()
  17. circle(10)
  18. ? square(10,20)+square(5,6)
input value
關於函數的輸入,我們先看以下的例子:
  1. data(1,2,null) # c沒有值
  2. func data(a, b, c)
  3. ? "Data a = " + a
  4. ? "Data b = " + b
  5. ? "Data c = " + c
這個例子我們可以學到幾點: 那麼若是有的變數不想或不需要輸入怎麼辦?此時可以將輸入改為一個list,例如:
  1. data2([:a=1, :b=2]) # 顯示c =
  2. func data2(alist)
  3. ? "Data a = " + alist[:a]
  4. ? "Data b = " + alist[:b]
  5. ? "Data c = " + alist[:c]
Variable scope
變數定義位置關係其影響範圍,全域可用稱全域變數(global varialbe),僅影響部分區域稱區域變數(local variable)
  1. gvar = 10
  2. func f1
  3. ? gvar+1 # 使用並印出全域變數
  4.  
  5. func f2 newValue
  6. gvar = newValue # 呼叫此函數來修改gvar的值
  7.  
  8. func f3
  9. for i=0 to 10 # i為區域變數
  10. ? i
  11. end
  12. see "i="+i # 但可在func之外取得值
  13.  
  14. func main
  15. f1()
  16. f2(100)
  17. ? gvar
  18. f3()
  19. see i # 錯誤,此處i為未定義變數
關於在loop內的變數,再看個例子:
  1. i = 100
  2. aloop()
  3. ? "i = " + i # i = 100
  4. func aloop()
  5. for i in 1:10
  6. ? i
  7. next
由例子中可以看出global variable i的值並不被函數內的i影響。但是之前的例子,不是在函數後印i會得到函數內的區域變數的值嗎?此處談一下Ring的程式架構,原則上我們的程式架構為最上方應該是load(Load Files),然後是程式碼(Statements and Global Variables,也可以放在main這個函數內),接下來是函數(Functions),最後是包裹及物件(Packages and Classes)。也就是說,我們不應在函數之後放置主程式碼,所以也不會有之前例子產生的情形。
Pure function
Pure function簡單的說是函數並不使用外部變數,所有操作都純粹包含在函數內。看個例子:
  1. lista = 1:5
  2. square()
  3. ? lista # lista 被改變
  4.  
  5. func square
  6. for i in lista # 使用global的lista
  7. i = i*i
  8. next i
  9. return lista
原則上原來的lista被改變了,而且使用了globle varialbe,呼叫square()所傳回的list可以是一個新的list,如下:
  1. func main()
  2. lista = 1:5
  3. listb = square(lista) # 產生新的list
  4. ? lista # lista 保持不變
  5. ? listb
  6.  
  7. func square(arg)
  8. alist = arg # 複製傳入的參數作為內部使用,避免影響外部之資料
  9. for i in alist # 使用global的lista
  10. i = i*i
  11. next i
  12. return alist
First-class function
在Ring中,函數是第一級的居民。我們可以將其存為一個變數,也可以當作另一個函數的輸入或傳回值。
  1. hi = "hi" # 使用"函數名"來儲存函數於變數中
  2. call hi("Mary") # 使用call來呼叫
  3. f = :fun # 或使用:函數名來儲存函數於變數中
  4. k = call f(10)
  5. ? k
  6.  
  7. func hi(name)
  8. ? "Greeting, " + name
  9.  
  10. func fun(a)
  11. return a*a
High-order function
High-order function可將其他函數當作輸入引數。
  1. ? finalScore(:sqrtScore, 36)
  2.  
  3. func sqrtScore(score) # 開根號
  4. return sqrt(score)
  5.  
  6. func finalScore(argFun, score)
  7. k = call argFun(score)
  8. return k*10 # 乘以10
一個無聊的程式,將成績開根號乘以10,一個函數開根號,一個將此函數作為輸入,將得到的值乘以10。
Anonymous Functions
無名函數,類似lambda,無須命名,如前所述,可將函數儲存於一變數中。
  1. square = func x,y {return x*y}
  2. ? call square(10,20) # 在函數定義之後call
需先定義函數,再在之後call(需使用call關鍵字來執行)。
  1. nums = 10:30 # list
  2. rem = map(nums, func n,d {return n*d}, 3) # 第二個引數為Anonymous function
  3. ? rem
  4.  
  5. func map(li, fun, di) # args: list, function, number
  6. for e in li
  7. e = call fun(e, di) # call func fun
  8. next
  9. return li
anonymous function做為另一個函數的輸入值。
Nested Functions
函數之內有函數。
  1. ? grade(36)
  2.  
  3. func grade(score)
  4. ss = func s {return sqrt(s)} # 將分數開根號
  5. return call ss(score)*10 # 呼叫函數ss並將傳回值*10
Fabricating Functions
我們可以藉由使用者輸入來改變函數內容,舉例如下:
  1. give formula # 取得使用者輸入的公式
  2. ? formula
  3. fstr = "func f(x) return " + formula # 建立函數的字串
  4. eval(fstr) # 執行函數
  5. ? ">> x = "
  6. while true
  7. give x # 取得x值
  8. if x="quit"
  9. see "See you."
  10. exit
  11. else
  12. ? "f("+x+") = " + f(x) + NL + NL + ">> x = " # 取得f(x)之值
  13. ok
  14. end
解釋一下eval()函數,此函數可以讓我們在程式進行時(runtime)執行一段程式碼(字串表示)。例如:
  1. eval("for i in 1:10 see i*i+NL next")
系統函數
Ring內建的系統函數

陣列(List)

List是Ring中的基本資料型態,之前已有用過,在此再做點介紹。
建立list。複習一下,我們已經知道可以使用以下方法建立list。
  1. List1 = [1,2,3,4,5]
  2. List2 = 1:10
  3. List3 = "z":"A"
  4. ? list1+NL+list2+NL+list3 # 大小寫同型
也可以使用list()函數來建立陣列
  1. list1 = list(5) # 長度為5的list
  2. ? list1 # 元素預設值為0
  3. list2d = list(2,3) # 2D陣列
  4. ? list2d

list內的元素存取其實沒甚麼好說的,主要要注意的是在Ring中,index是由1開始起算的。
  1. List1 = 1:10 # 建立一個list
  2. see List1[1]+NL # 使用index取得對應的值(Note: Ring的index自1開始)
  3. List1[1] = 99 # 改變list中某位置的值
  4. see List1

加入元素。
  1. List1 = 1:5
  2. add(list1, 6) # 使用add()函數加入元素
  3. List1 + 7 # 如前所述,也可使用+號直接加入元素
  4. ? list1
  5. insert(list1, 0, 100) # 在第0位置加入元素100,將成為list中第一個元素
  6. ? list1
  7. insert(list1, len(list1), 99) # 在最後一位加入元素99,Len()傳回list長度
  8. ? list1

刪除元素。
  1. List1 = "a":"f"
  2. del(list1, 3) # 刪除index=3位置的元素,之後元素須往前補
  3. ? list1
  4. ? "list1[3] = " + list1[3]
沒甚麼好說的,就是使用del()函數。若是要刪除list內所有元素,可以如下:
  1. List1 = "a":"f"
  2. ? "list1 = "
  3. ? list1
  4. k = len(list1) # 先取得list1的長度
  5. for i=1 to k
  6. del(list1, 1)
  7. next
  8. ? "list1 = "
  9. ? list1
事實上更簡單的方式為:
  1. list1 = []

搜尋list。使用find()或BinarySearch()函數。
  1. ab = []
  2. for L in "a":"z" step 3
  3. ab + L
  4. next
  5. ? ab
  6.  
  7. ? find(ab, "j") # 傳回欲搜尋元素之index
  8. ? Binarysearch(ab, "p") # 使用BinarySearch,傳回欲搜尋元素之index
  9.  
  10. ab2 = []
  11. i = 1
  12. for L in "a":"z" step 3
  13. ab2 + [i, L]
  14. i++
  15. next
  16. ? ab2
  17.  
  18. ? find(ab2, "j", 2) # 搜尋每個元素的第2欄(第2個子元素),傳回該元素所在之index
  19. ? Binarysearch(ab2, "p", 2) # 使用BinarySearch,傳回欲搜尋元素之index

Reverse & Sort。
  1. list1 = 1:5
  2. list1 = reverse(list1) # 將list1倒置
  3. ? list1
  4.  
  5. list1 = sort(list1) # 將list1由小到大排序
  6. ? list1
  7. ? "============================"
  8. ab2 = []
  9. for L in "a":"z" step 3
  10. ab2 + [random(26), L]
  11. next
  12. ? ab2
  13. ? "----------------------------"
  14. ab2 = reverse(ab2) # 將ab2倒置
  15. ? ab2
  16. ? "----------------------------"
  17. ab2 = sort(ab2, 1) # 根據第1個欄位排序(改為2則根據第二個欄位排序)
  18. ? ab2

Access List Items by String Index: 之前提過的,原則上就是dict in Python。
  1. alist = [:one=1, :two=2, :three=3]
  2. ? alist[:two]
  3. alist[:four]=4 # 加上新元素
  4. ? alist
  5. # traversal
  6. for ele in alist
  7. ? "alist["+ele[1]+"] = " + ele[2]
  8. end
  9.  
  10. # 原來的長相如下:
  11. blist = [["one", 1],["two", 2],["three", 3]]
  12. ? blist["one"]
  13. blist["four"]=4 # 加上新元素
  14. ? blist
  15. # traversal
  16. for ele in blist
  17. ? "blist["+ele[1]+"] = " + ele[2]
  18. end

字串(String)

這裡介紹一些關於字串的操作。
建立字串。用""、''、``都可以。
  1. s1 = "String1"
  2. s2 = 'String2'
  3. s3 = :String3
  4. s4 = "do 're' mi"
  5. s5 = `"do" 're' "mi"`
  6. ? s1+NL+s2+NL+s3+NL+s4+NL+s5

len()、lower()、upper()、取得修改字母:原則上與list操作相同
  1. s1 = "String1"
  2. s1 = lower(s1) # 轉換成小寫
  3. ? s1
  4. s1 = upper(s1) # 轉換成大寫
  5. ? s1
  6. for i=1 to len(s1) step 2
  7. s1[i] = lower(s1[i]) # 取得修改字串的值
  8. next
  9. ? s1

substring: trim()、left()、right()、substr()
  1. s1 = " This is a String. "
  2. lefts = left(trim(s1), 4) # 先使用trim()去除s1兩邊的空白
  3. ? lefts
  4. rights = right(trim(s1), 7)
  5. ? rights
  6. ## 使用substr()
  7. ? substr(s1, "is") // 取得is出現的位置,6,若不包含該子字串則傳回0
  8. ? substr(s1, 6) // 取得s1中自6開始到最後的子字串(is is a String. ) >> 取中間到最後一段
  9. ? substr(s1, 6, 5) // 取得自第6位開始後5個字元的子字串(is is) >> 取中間一段。原則上沒給5等於上一例,預設值為len(s1)-6
  10. ? substr(s1, "is", "are") // 將s1中的"is"字串用"are"取代( Thare are a String. )

copy()、lines()
  1. s1 = "One
  2. More
  3. Night"
  4.  
  5. ? lines(s1) // 算出字串有幾行 >> 3 lines
  6. ? copy(s1, 3) // 複製字串3
  7. ? lines(copy(s1,3)) // 7 lines

strcmp():比較兩個字串
  1. # 比較字串是否相同,相同傳回0,前比後大傳回1,反之傳回-1>> 0, 1, -1
  2. ? strcmp("do", "do")+NL+strcmp("re", "mi")+NL+strcmp("fa", "sol")

str2list()、list2str(): 字串與list轉換。注意str2list產生的list中的元素為原str中的一行(非一個字元)。
  1. str = "whatever"
  2. slist = str2list(str)
  3. add(slist, "do") # 使用list函數add()來加入新字串
  4. ? slist
  5. ? "Elements in slist = " + len(slist)
  6. newstr = list2str(slist)
  7. ? "lines in newstr = " + lines(newstr) # 使用字串函數lines()來檢查總行數

數學函數與日期時間

Ring提供數學函數供使用,應該沒甚麼好說明的,僅表列如下。可參考官網之說明。
Function Description
sin(x) Returns the sine of an angle of x radians
cos(x) Returns the cosine of an angle of x radians
tan(x) Returns the tangent of an angle of x radians
asin(x) Returns the principal value of the arc sine of x, expressed in radians
acos(x) Returns the principal value of the arc cosine of x, expressed in radians
atan(x) Returns the principal value of the arc tangent of x, expressed in radians
atan2(y,x) Returns the principal arc tangent of y/x, in the interval [-pi,+pi] radians
sinh(x) Returns the hyperbolic sine of x radians
cosh(x) Returns the hyperbolic cosine of x radians
tanh(x) Returns the hyperbolic tangent of x radians
exp(x) Returns the value of e raised to the xth power
log(x) Returns the natural logarithm of x (the base of e)
log(x,b) Returns the logarithm of x to the base of b
log10(x) Returns the common logarithm (base-10 logarithm) of x
ceil(x) Returns the smallest integer value greater than or equal to x
floor(x) Returns the largest integer value less than or equal to x
fabs(x) Returns the absolute value of x.
pow(x,y) Returns x raised to the power of y
sqrt(x) Returns the square root of x
random(x) Returns a random number in the range [0,x]
srandom(x) Initialize random number generator
unsigned(n,n,c) Perform operation using unsigned numbers
decimals(n) Determine the decimals digits after the point in float/double numbers
日期時間
直接練習下例。
  1. ? time() # 顯示目前時間
  2. ? Date() # 顯示今日日期
  3. ? addDays(date(), 1) # 顯示明天(+1天)
  4. ? addDays(date(), -1) # 昨天
  5. birthday = "10/10/2021"
  6. ? "到今年雙十還有"+diffdays(birthday, date())+"天" # 兩個日期的差
  7. ? clockspersecond() # 1000, 每一秒1000ticks(毫秒)
  8. t1 = clock() # 自開始程式到目前經過多少ticks(毫秒)
  9. ? "t1 = " + t1
  10. // # 暫停兩秒
  11. while clock() - t1 <= 2*clockspersecond()
  12.  
  13. end
  14. duration = clock() - t1 # 從t1到目前時間(ticks/1000=秒數)
  15. ? "duration = " + duration
  16.  
  17. tlist = timelist() # 包含所有時間資訊的list
  18. for i=1 to len(tlist)
  19. ? ""+i+" "+tlist[i]
  20. next
  21.  
  22. # 設計另一個list表示對應位置的資訊(可僅列我們所需要的), 例如:
  23. ti = [:week=2, :month=4, :datetime=5, :day=6, :hour=7, :minute=11, :second=13, :date=16, :time=17, :year=18]
  24. # 可以自己組合需要的時間資訊
  25. ? timelist()[ti[:date]]
  26. ? timelist()[ti[:week]]+" "+timelist()[ti[:date]]+" "+timelist()[ti[:time]]

timelist每個元素的對應資訊:
index value
1 abbreviated weekday name
2 full weekday name
3 abbreviated month name
4 full month name
5 Date & Time
6 Day of the month
7 Hour (24)
8 Hour (12)
9 Day of the year
10 Month of the year
11 Minutes after hour
12 AM or PM
13 Seconds after the hour
14 Week of the year (sun-sat)
15 day of the week
16 date
17 time
18 year of the century
19 year
20 time zone
21 percent sign

物件

Ring當然也可以建立物件,此處期待你已有物件之概念,不多贅述,直接看做法。
第I種寫法:
  1. new Node { # 產生一個無名的物件
  2. x = 10
  3. y = 20
  4. print()
  5. }
  6.  
  7. n2 = new Node # 產生另一個物件
  8. n2.id = 2
  9. n2.x = 30
  10. n2.print()
  11.  
  12. class Node
  13. id=1 x y=60 # attribute,變數間用空白隔開,可給預設值
  14. func print() # method
  15. see "id=" + id + " x=" + x + " y=" + y + NL

第II種寫法:
  1. new Vertex(3,30,20) # 直接呼叫init()產生一無名物件
  2. v2 = new Vertex(5, 50, 60) # 建立另一個物件
  3. class Vertex
  4. id x y # attribute,變數間用空白隔開,可給預設值
  5. func init a, b, c
  6. id = a
  7. x = b
  8. y = c
  9. print()
  10. func print
  11. see "id=" + id + " x=" + x + " y=" + y + NL

第III種寫法:
  1. p = new point([:id = 8, :x = 11, :y = 12]) # 傳入一個list
  2. p.print()
  3.  
  4. class Point
  5. id x y
  6. func init par # 使用list作為傳入值
  7. id = par[:id]
  8. x = par[:x]
  9. y = par[:y]
  10. func print()
  11. see "id=" + id + " x=" + x + " y=" + y + NL
class內的變數初始預設值為null。
範例:包含使用setters&getters,private attributes&methods,composition(使用另一個物件作為一個物件的attribute)
  1. v = new Vertex(5, 50, 60) # 建立一個vertex物件
  2. ## test the vertex
  3. ? v.toString() # print out vertex info
  4. v.x = 100 # 直接修改x的值,因為會呼叫setx()
  5. v.y = 200 # 直接修改y的值,因為會呼叫sety()
  6. ? "x = " + v.x + " y = " + v.y + NL # 直接得到x與y的值,因為會呼叫getx()、gety()
  7. ? "id = " + v.getid()
  8. // v.setId(18) # Error! 自外部呼叫private method
  9. u = new Vertex(7, 11, 22) # 建立另一個vertex物件
  10. ## about the arc
  11. arc = new Arc(u, v) # Arc的引數為兩個vertex物件
  12. ? arc.toString() # print out arc info
  13.  
  14. class Vertex
  15. x y # attribute,變數間用空白隔開,可給預設值
  16. func init a, b, c
  17. id = a # id 是private variable
  18. x = b
  19. y = c
  20. # setters & getters
  21. func setX newX
  22. x = newX
  23. func getX
  24. return x
  25. func setY newY
  26. y = newY
  27. func getY
  28. return y
  29. func getId
  30. return id
  31. func toString()
  32. return "id=" + id + " x=" + x + " y=" + y + NL
  33. # 以下區塊為 private attributes & methods
  34. private
  35. id
  36. func setId newId
  37. id = newId
  38.  
  39. class Arc
  40. va vb
  41. func init a, b
  42. va = a
  43. vb = b
  44. func length()
  45. return sqrt((va.x-vb.x)*(va.x-vb.x)+(va.y-vb.y)*(va.y-vb.y))
  46. func toString()
  47. return va.toString()+NL+vb.toString()+NL+"Length = " + length()

override operator這個method來定義符號,需要兩個物件作為輸入參數。下例僅供練習,因為+、-、*、/會傳回新的物件,而>與<則會傳回boolean,不是一個很好的設計。
  1. one = new onething(10,20) # 建立一個物件
  2. two = new onething(100, 200) # 建立另一個物件
  3. three = one + two # 使用operator建立另一個物件
  4. ? three
  5. ? one > two # 使用>或<判斷兩物件大小
  6. class Onething
  7. a b
  8. func init m,n
  9. a = m
  10. b = n
  11. func operator op, anotherthing
  12. result = new Onething
  13. switch op
  14. on "+"
  15.  
  16. result.a = a+anotherthing.a
  17. result.b = b+anotherthing.b
  18. on "-"
  19.  
  20. result.a = a-anotherthing.a
  21. result.b = b-anotherthing.b
  22. on "*"
  23.  
  24. result.a = a*anotherthing.a
  25. result.b = b*anotherthing.b
  26. on "/"
  27.  
  28. result.a = a/anotherthing.a
  29. result.b = b/anotherthing.b
  30. on ">"
  31. if a > anotherthing.a
  32. ? ""+a + ">" + anotherthing.a
  33. return true
  34. else
  35. ? ""+a + "<" + anotherthing.a
  36. return false
  37. ok
  38. on "<"
  39. if a < anotherthing.a
  40. ? ""+a + "<" + anotherthing.a
  41. return true
  42. else
  43. ? ""+a + ">" + anotherthing.a
  44. return false
  45. ok
  46. off
  47. return result
  48.  
  49. func toString()
  50. return ""+a+" "+b

Inheritance:繼承使用關鍵字from
  1. Peter = new Spiderman{ # new一個物件
  2. name = "Peter Parker"
  3. age = 17
  4. strength = 86
  5. skills = ["Accelerated healing", "Precognitive", "Utilizing wrist-mounted web-shooters"]
  6. }
  7. ? peter.toString()
  8.  
  9. # new另一個物件(Hero部分的變數使用預設值)
  10. peter2 = new spiderman(["Accelerated healing", "Precognitive", "Utilizing wrist-mounted web-shooters"])
  11. # 修改預設值
  12. peter2.name = "peter2"
  13. ? peter2.toString()
  14.  
  15. class Hero # 一個名為Hero的物件
  16. name="Peter Parker" age=17 strength = 80
  17. func init n,a, stren
  18. name = n
  19. age = a
  20. strength = stren
  21. func toString()
  22. return "Name = " + name + " Age = " + age + " Strength = " + strength + NL
  23.  
  24. class Spiderman from Hero # 繼承自Hero的物件
  25. skills
  26. func init skillsList
  27. skills = skillsList
  28. func toString()
  29. skill = ""
  30. count = 1
  31. for s in skills
  32. skill = skill + count + ". " + s + NL
  33. count++
  34. next
  35. return super.toString() + skill

Package: 可以將所有的class放在一個package內,只要使用package關鍵字並定義名稱即可。如此可以較容易收納。好像無法直接在package內定義函數,不過我們可以如例中將函數收納在一個class內,然後使用時再呼叫。
  1. v1 = new graph.vertex(1,2,3)
  2. v2 = new graph.vertex(7,8,9)
  3. ? v1.toString() + v2.toString()
  4. a1 = new graph.arc(v1,v2)
  5. ? "a1.length = " + a1.length
  6.  
  7. f = new graph.funs # 建立funs物件然後呼叫相關函數
  8. f.hello("Mary")
  9. ? f.mul(10,20)
  10.  
  11. package graph
  12.  
  13. class Funs # 放置需要的函數之物件
  14. func Hello(name)
  15. see "Hi, " + name + NL
  16. func mul(a,b)
  17. return a*b
  18.  
  19. class Vertex
  20. id x y # attribute,變數間用空白隔開,可給預設值
  21. func init a, b, c
  22. id = a
  23. x = b
  24. y = c
  25. # setters & getters
  26. func setX newX
  27. x = newX
  28. func getX
  29. return x
  30. func setY newY
  31. y = newY
  32. func getY
  33. return y
  34. func toString()
  35. return "id=" + id + " x=" + x + " y=" + y + NL
  36. # private attributes & methods
  37. private
  38. func setId newId
  39. id = newId
  40.  
  41. class Arc
  42. va vb length
  43. func init a, b
  44. va = a
  45. vb = b
  46. length = length()
  47. func length()
  48. return sqrt((va.x-vb.x)*(va.x-vb.x)+(va.y-vb.y)*(va.y-vb.y))
  49. func toString()
  50. return va.toString()+NL+vb.toString()+NL+"Length = " + length()
每次要使用package內的class,都要在之前加上package的名稱,為了簡化,可以先使用import將package導入,然後便可直接使用了。以下修改上例中package之前的程式碼。
  1. import graph
  2. v1 = new Vertex(1,2,3)
  3. v2 = new Vertex(7,8,9)
  4. ? v1.toString() + v2.toString()
  5. a1 = new arc(v1,v2)
  6. ? "a1.length = " + a1.length
  7. f = new funs # 建立funs物件然後呼叫相關函數
  8. f.hello("Mary")
  9. ? f.mul(10,20)
簡化了許多。
當我們要處理好幾個instances時,可以將這些instances放在一個list(collection)內,因為可能要對於這個list做一些操作,所以做法可以在建立一個class,讓此list作為物件變數,然後建立物件方法來操作它(也可參考javapython之資料結構做法)。此外,此例中也介紹了sort()與find()函數。
  1. names = ["Spider man", "Iron man", "Super man", "Bat man", "Captain America"]
  2. ages = [:spiderman=17, :ironman=41, :superman=27, :batman=33, :captain=90]
  3. herosList = []
  4. for i=1 to len(names)
  5. add(herosList, new hero {name = names[i] age = ages[i][2]})
  6. next
  7.  
  8. _heros = new heros(herosList) # create a new instance containing heros
  9. see _heros.toString()
  10.  
  11. see _heros.sortby(:age) # or "age"
  12.  
  13. ? _heros.searchfor(27, :age) # search for one age = 27
  14.  
  15. class hero
  16. name age
  17. def toString()
  18. return name + " " + age
  19.  
  20. class heros
  21. hsList = []
  22. def init(hs)
  23. hsList = hs
  24. def getHeros()
  25. return hsList
  26. def sortBy(att)
  27. # 使用函數sort(List:list to sort, nColumn:用於多維, cAttribute:屬性)來排序
  28. return sort(hsList, 1, att)
  29. func searchfor(value, attribute)
  30. # 使用函數find(List:list to sort, ItemValue:值, nColumn:用於多維, cAttribute:屬性)來搜尋
  31. return find(hsList, value, 1, attribute)
  32.  
  33. def toString()
  34. s = ""
  35. for h in hsList
  36. s += h.toString() + NL
  37. next
  38. return s

self & this
  1. name = "Tony Stark" # Global variable
  2. age = 41 # Global variable
  3.  
  4. spiderman = new hero("peter parker", 17)
  5. see spiderman
  6.  
  7. ? spiderman.name+" "+spiderman.age
  8. spiderman.setname("Jonny") ## 無法使用spiderman.namd = "Jonny"
  9. ? spiderman.name
  10.  
  11. class hero
  12. self.name self.age ## 使用self修飾避免與global variable衝突
  13. func init(aname, newage)
  14. self.name = aname
  15. self.age = newage
  16. func setname(aname)
  17. self.name = aname
  18. func getname()
  19. return self.name
  20. func setage(newage)
  21. self.age = newage
  22. func getage()
  23. return self.age
self主要是要避免跟globle variable的名稱衝突,事實上並不見得需要使用。那麼this呢?
  1. spiderman = new hero{name = "peter parker" age=18}
  2. ? spiderman.info()
  3.  
  4. class hero
  5. name age attackpower = 80
  6. def info
  7. ? "name = " + self.name + " age = " + age + "attack power = " + attackpower + NL
  8. w = new weapon{
  9. ? "name = " + self.name # weapon 裡面的self表示weapon這個class
  10. ? "owner = " + this.name # this 表示hero這個class
  11. ? "attack power" + this.attackpower
  12. }
  13.  
  14. class weapon
  15. name = "web shooter"
  16. attackpower = 100
  17. owner
也可以用this替代self。
  1. spiderman = new hero{name="Peter Parker" age = 17}
  2. ? spiderman
  3. ? spiderman.toString()
  4.  
  5. class hero
  6. this.name this.age
  7. func toString()
  8. return name + " " + age
Note: 原則上Ring的語法不太需要使用self&this。

IO

簡單的輸出入例如see、give、put、get等不再贅述。
使用load:把需要的函數及class寫在一個檔案內(e.g. fun.ring),在主程式使用load關鍵字來導入即可使用。例如:
  1. load "fun.ring" # 在同一資料夾有一fun.ring檔案
  2. hello("Mary") # fun.ring內的函數
  3.  
  4. v1 = new vertex(1,2,3) # fun.ring內的class
  5. v2 = new vertex(2,10,20)
  6. v1.x = 11
  7. v1.y = 12
  8. ? v1.toString()
  9. a = new arc(v1, v2) # fun.ring內的class
  10. ? a.toString()
如果fun.ring內的classes是定義在一個package內(e.g. package fun.classes),那再使用時可以在load之後import這個package即可使用。
  1. load "fun.ring" # 在同一資料夾有一fun.ring檔案
  2. import fun.classes
我們可以load超過一個檔案,不過當兩個檔案中有同名的函數(或物件),則會產生錯誤。不像其他語言(例如python)可以定義Name space,使用Ring時設計名稱不要重複即可。
Files:此處簡單介紹一些常見的檔案操作,其他詳細的函數列表請參考官網