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。 這裡可以先學會幾件事,- ?、see、 put都可以顯示字串,若要使用print(),需先導入stdlib.ring模組
- #、//皆可做單行註解,多行註解使用/* */
- 我們可以使用其中一種,也可以在不同情況交互使用不同語法
- 關鍵字語法無大小寫限制
- 可以直接顯示多行字串或使用nl換行(使用+號連結字串與newline符號)
在底下的Input輸入欄中輸入然後按send。
? "What's your name?" give yourname ? "Hello " + yourname put "How old are you?" + nl get age put "You are " + age + " years old." + nl Load "stdlib.ring" print("What is your favorite color?"+nl) fcolor = GetString() print("#{fcolor} is your favorite color")
變數與資料型態
Ring的基本變數型態為numbers, strings, objects, and lists。宣告時無須指定變數型態。numbers:包含整數、實數。
a~e都是數字。
a = 1 b = 3.14 c = -10 d = 100/7 e = 0 see "a = " + a + nl + "b = " + b + NL + "c = " + c + nL + "d = " + d + Nl + "e = " + e
strings:使用雙引號包括的為字串。
s1 = "String" // 字串 s2 = time() # 目前時間 s3 = date() // 目前日期 s4 = "" # 空字串 ? s1 + NL + s2 + NL + s3 + NL + s4
- "10"為字串,若是數字+字串(e.g. 10+"10")或字串+數字(e.g. "10"+10),則型態與第一個相同。
n1 = "10" n2 = 10 see n1+n2+NL see n2+n1+NL
objects:之後再提。
lists:先簡單介紹。
alist = [1,2,3] blist = "a":"f" # 產生a到f的list ? alist + blist ? alist[4] ? Len(alist)
- "a":"f"或1:10可直接建立序列的list
- 兩個list相加,原則上後面的變為前一個的最後一個元素
- 使用len()函數來取得list長度
好像少了boolean,原則上初始值true=1,false=0。
? true see False
- true跟false為兩變數,true的預設值為1,原則上不等於0的皆為true
- Ring允許你改變true跟false的值,不過隨便改變可能造成整個程式的大錯亂,宜慎之
在Ring中,null的涵義如下:
s = "" s1 = "String" n = "null" ? isNULL(s) ? isNULL(s1) ? isNULL(n) ? isNull(null) # null為變數,內定值為null ? s1 + null
- 空字串與字串"null"為null
- 可以使用isNull()來判斷是否為null
- null為變數,表示其值可改,again,最好別亂改
使用以下函數來確認變數型態: type()、isString()、isNumber、isList()、isNull()。
此外還可以使用官網中(https://ring-lang.sourceforge.io/doc1.10/checkandconvert.html)羅列的函數來確認資料內容,可以自己練習看看,之後若有機會用到再介紹。
n1 = 10 n2 = 3.1416 n3 = 0 n4 = 100/7 s1 = "String" s2 = "null" List1 = 1:10 see type(n1)+NL+type(s1)+NL+type(s2)+NL+type(List1)+NL put isString(n4)+NL+isString(s1)+NL+isString(s2)+NL ? isList(List1)+NL+isList(n2) ? isNull(n3)+NL+isNull(s2)
若欲將某變數型態改變為另一型態,使用以下函數:number()、string()、ascii()、char()、hex()、dec()、str2hex()、hex2str()。
a = "20" a = number(a) ? type(a) a = string(a) ? type(a) b = "A" ? ascii(b) // ascii碼 ? char(109) // ascii碼所代表的符號 ? hex(20) // 十進位數字轉為16進位數字 ? dec("14") // 16進位數字(字串顯示)轉為十進位數字 ? str2hex("String") // 537472696e67 ? hex2str("537472696e67") // String
命名
命名的慣例在各個語言中大多類似,在此不再贅述。運算子
Ring的運算子與其他許多語言多有類似,以下僅做簡單解釋:- Arithmetic Operators:
+ - * / % ++ -- - Relational Operators:注意判斷相等是用=而非==。
= != > < >= <= - Logical Operators:
and && or || ! not - Bitwise Operators:
& | ^ ~ << >> - Assignment Operators:
= += -= *= /= %= <<= >>= &= |= ^=
運算優先順序
Operator |
---|
.[](){} |
- ~ :Literal [list items] |
++ -- |
Start:End |
* / % |
+ - |
<< >> |
& |
| ^ |
< > <= >= |
= != |
not ! |
and or && || |
Assignment = += -= *= /= %= >>= <<= &= ^= |= |
? |
Control Flow
if statement
Ring的條件式,可以有如下寫法:I: if...but...else...ok
? "你最喜歡的季節? 1. 春 2. 夏 3. 秋 4. 冬 " give season if season = 1 see "你最喜歡的季節是春天" but season = 2 see "你最喜歡的季節是夏天" but season = 3 see "你最喜歡的季節是秋天" else see "你最喜歡的季節是冬天" ok
II: if...elseif...else...ok
? "你最喜歡的季節? 1. 春 2. 夏 3. 秋 4. 冬 " give season if season = 1 see "你最喜歡的季節是春天" elseif season = 2 see "你最喜歡的季節是夏天" elseif season = 3 see "你最喜歡的季節是秋天" else see "你最喜歡的季節是冬天" ok
III: if {}
? "你最喜歡的季節? 1. 春 2. 夏 3. 秋 4. 冬 " give season if season = 1 { see "你最喜歡的季節是春天" elseif season = 2 see "你最喜歡的季節是夏天" elseif season = 3 see "你最喜歡的季節是秋天" else see "你最喜歡的季節是冬天" }
- I與II的分別是可以使用but替代elseif。
- III與前兩者之分別是使用{}可以省略ok(二擇一使用)。
? "請問你的體重是幾公斤?" get w ? "請問你的身高是幾公尺?" get h bmi = w/h/h ? "" if bmi <= 18.5 see "太瘦" but bmi > 18.5 and bmi <= 23.9 see "標準體重" elseif bmi > 23.9 and bmi < 27.9 see "有點過重" else see "胖" ok
switch
switch跟if else作用類似,一樣有三種寫法:I: switch...on...other...off
? "你最喜歡的季節? 1. 春 2. 夏 3. 秋 4. 冬 " give season switch season on 1 () see "你最喜歡的季節是春天" on 2 see "你最喜歡的季節是夏天" on 3 see "你最喜歡的季節是秋天" on 4 see "你最喜歡的季節是冬天" other see "請重新選擇" off // 使用off來關閉switch
II: switch...case...else...end
? "你最喜歡的季節? 1. 春 2. 夏 3. 秋 4. 冬 " give season switch season case 1 () see "你最喜歡的季節是春天" case 2 see "你最喜歡的季節是夏天" case 3 see "你最喜歡的季節是秋天" case 4 see "你最喜歡的季節是冬天" else // 可與other替換 see "請重新選擇" end // 可與off替換
III: switch{}
? "你最喜歡的季節? 1. 春 2. 夏 3. 秋 4. 冬 " give season switch season { case 1 see "你最喜歡的季節是春天" on 2 // case與on效果相同 see "你最喜歡的季節是夏天" case 3 see "你最喜歡的季節是秋天" on 4 see "你最喜歡的季節是冬天" else // 可與other替換 see "請重新選擇" } // 有大括號則不使用off或end
while loop
Loop一樣分while loop與for loop,不囉嗦,直接看例子學語法:I: while...end
n=1 while n<10 see n+NL n++ end # 使用end來作為while之結束 while True ? "guess big or small (quit to leave)" get bs if bs = "quit" see "See you next time." exit # 使用exit來離開while loop else computer = random(12)+1 # 產生1-13之隨機整數 if bs = "big" if computer >= 7 see "You win>> "+computer+NL else see "You lose>> "+computer+NL ok elseif bs = "small" if computer <=7 see "You win>> "+computer+NL else see "You lose>> "+computer+NL ok else see "Unknown instruction." ok ok end
II: while{}
好像只有兩種寫法。
n=1 while n<10{ see n+NL n++ } // 使用{}則不使用end while True { ? "guess big or small (quit to leave)" get bs if bs = "quit" see "See you next time." exit # 使用exit來離開while loop else computer = random(12)+1 # 產生1-13之隨機整數 if bs = "big" if computer >= 7 see "You win>> "+computer+NL else see "You lose>> "+computer+NL ok elseif bs = "small" if computer <=7 see "You win>> "+computer+NL else see "You lose>> "+computer+NL ok else see "Unknown instruction." ok ok } // 使用{}則不使用end
for loop
試試看for loopI: for...next
for i=1 to 10 put i+NL next # 用next表示往下一個 for i=1 to 10 step 2 # step表步幅,可以是負的(i由大到小) see i+NL next lista = 1:10 for i in lista # in 表示在list中 ? i next for i in lista step 2 ? i next for i in lista if i%2=0 i="none" ok # 表示我們可以在for loop內修改lista內的值 next put lista
II: for...end
for i=1 to 10 put i+NL end # 用end替代next for i=1 to 10 step 2 # step表步幅,可以是負的(i由大到小) see i+NL end lista = 1:10 for i in lista # in 表示在list中 ? i end for i in lista step 2 ? i end for i in lista if i%2=0 i="none" ok # 表示我們可以在for loop內修改lista內的值 end put lista
III: for{}
for i=1 to 10{ put i+NL } # 用{}替代end與next for i=1 to 10 step 2{ # step表步幅,可以是負的(i由大到小) see i+NL } lista = 1:10 for i in lista{ # in 表示在list中 ? i } for i in lista step 2{ ? i } for i in lista{ if i%2=0 i="none" ok # 表示我們可以在for loop內修改lista內的值 } put lista
exit
已知exit可以用來離開loop,尚可以使用exit 2來離開兩層nested的loop,例如:
for i=1 to 10 for j=1 to 10 see "" + i + "*" + j + "=" + i*j + NL if i*j>50 exit 2 ok next end
do...again
原則上跟其他語言的do...while相同,先執行一次再判斷,可保證至少執行一次
x = 1 do ? x x++ again x < 5 do # x=5 now,至少做一次 ? x x-- again x>5
loop
loop就是continue,直接看例子。
for i in 1:10 if i%2=0 loop # 開始新的iteration else ?i ok end
函數(function)
函數應該不陌生,來看Ring的寫法:I: func
hello() # 在函數定義的前面呼叫 circle(10) ? square(10,20)+square(5,6) func hello see "Hi, there"+NL func circle r see 3.1416*r*r+NL func square l,w return l*w # 使用return傳回值
II: def...[end]
hello() # 在函數定義的前面呼叫 circle(10) ? square(10,20)+square(5,6) def hello see "Hi, there"+NL end def circle r see 3.1416*r*r+NL # end可有可無 def square l,w return l*w # 使用return傳回值 end
III:func[{}]
hello() # 在函數定義的前面呼叫 circle(10) ? square(10,20)+square(5,6) func hello{ see "Hi, there"+NL } # 很顯然{}可有可無 def circle r{ see 3.1416*r*r+NL } func square l,w{ return l*w # 使用return傳回值 }
main function
可有可無的函數,應該是保留C語言的習慣,可用作收納整理程式碼
func hello{ see "Hi, there"+NL } # 很顯然{}可有可無 def circle r{ see 3.1416*r*r+NL } func square l,w{ return l*w # 使用return傳回值 } func main put "先執行函數定義之前之程式指令,然後再執行此處之指令"+NL see "若函數定義之前無程式指令,則由main開始執行,此函數可有可無"+NL hello() circle(10) ? square(10,20)+square(5,6)
input value
關於函數的輸入,我們先看以下的例子:這個例子我們可以學到幾點:
data(1,2,null) # c沒有值 func data(a, b, c) ? "Data a = " + a ? "Data b = " + b ? "Data c = " + c
- 函數的變數可以使用小括號()括起來。
- 函數需要三個變數,我們就得傳入三個,即使c的值是空,還是得傳入null,否則出現錯誤。
data2([:a=1, :b=2]) # 顯示c = func data2(alist) ? "Data a = " + alist[:a] ? "Data b = " + alist[:b] ? "Data c = " + alist[:c]
- 在list內的值前面給特定名稱(名稱須以:開頭),之後可以使用此名稱來取得對應的值,而不需要使用index,類似python中的dict。
- 此時傳入函數之個數不足三個並不會產生錯誤。
Variable scope
變數定義位置關係其影響範圍,全域可用稱全域變數(global varialbe),僅影響部分區域稱區域變數(local variable)關於在loop內的變數,再看個例子:
gvar = 10 func f1 ? gvar+1 # 使用並印出全域變數 func f2 newValue gvar = newValue # 呼叫此函數來修改gvar的值 func f3 for i=0 to 10 # i為區域變數 ? i end see "i="+i # 但可在func之外取得值 func main f1() f2(100) ? gvar f3() see i # 錯誤,此處i為未定義變數
由例子中可以看出global variable i的值並不被函數內的i影響。但是之前的例子,不是在函數後印i會得到函數內的區域變數的值嗎?此處談一下Ring的程式架構,原則上我們的程式架構為最上方應該是load(Load Files),然後是程式碼(Statements and Global Variables,也可以放在main這個函數內),接下來是函數(Functions),最後是包裹及物件(Packages and Classes)。也就是說,我們不應在函數之後放置主程式碼,所以也不會有之前例子產生的情形。
i = 100 aloop() ? "i = " + i # i = 100 func aloop() for i in 1:10 ? i next
Pure function
Pure function簡單的說是函數並不使用外部變數,所有操作都純粹包含在函數內。看個例子:原則上原來的lista被改變了,而且使用了globle varialbe,呼叫square()所傳回的list可以是一個新的list,如下:
lista = 1:5 square() ? lista # lista 被改變 func square for i in lista # 使用global的lista i = i*i next i return lista
func main() lista = 1:5 listb = square(lista) # 產生新的list ? lista # lista 保持不變 ? listb func square(arg) alist = arg # 複製傳入的參數作為內部使用,避免影響外部之資料 for i in alist # 使用global的lista i = i*i next i return alist
- 此處的重點是使用alist = arg來取得外部的變數做內部使用,而不會與外部資訊相互影響。
- 在Ring中,=表示的是deep copy,亦即複製資料至另一塊獨立的記憶體(傳入函數的list或object參數預設為傳址而非傳值)。例如:
- ori = [1,"k":"r",2,['a','b','c'],3]
- copyed = ori # Deep copy
- copyed[2][1] = "A"
- ? copyed # copyed中的k變成了A
- ? ori # 保持原資料內容不變
First-class function
在Ring中,函數是第一級的居民。我們可以將其存為一個變數,也可以當作另一個函數的輸入或傳回值。
hi = "hi" # 使用"函數名"來儲存函數於變數中 call hi("Mary") # 使用call來呼叫 f = :fun # 或使用:函數名來儲存函數於變數中 k = call f(10) ? k func hi(name) ? "Greeting, " + name func fun(a) return a*a
High-order function
High-order function可將其他函數當作輸入引數。一個無聊的程式,將成績開根號乘以10,一個函數開根號,一個將此函數作為輸入,將得到的值乘以10。
? finalScore(:sqrtScore, 36) func sqrtScore(score) # 開根號 return sqrt(score) func finalScore(argFun, score) k = call argFun(score) return k*10 # 乘以10
Anonymous Functions
無名函數,類似lambda,無須命名,如前所述,可將函數儲存於一變數中。需先定義函數,再在之後call(需使用call關鍵字來執行)。
square = func x,y {return x*y} ? call square(10,20) # 在函數定義之後call
anonymous function做為另一個函數的輸入值。
nums = 10:30 # list rem = map(nums, func n,d {return n*d}, 3) # 第二個引數為Anonymous function ? rem func map(li, fun, di) # args: list, function, number for e in li e = call fun(e, di) # call func fun next return li
Nested Functions
函數之內有函數。
? grade(36) func grade(score) ss = func s {return sqrt(s)} # 將分數開根號 return call ss(score)*10 # 呼叫函數ss並將傳回值*10
Fabricating Functions
我們可以藉由使用者輸入來改變函數內容,舉例如下:解釋一下eval()函數,此函數可以讓我們在程式進行時(runtime)執行一段程式碼(字串表示)。例如:
give formula # 取得使用者輸入的公式 ? formula fstr = "func f(x) return " + formula # 建立函數的字串 eval(fstr) # 執行函數 ? ">> x = " while true give x # 取得x值 if x="quit" see "See you." exit else ? "f("+x+") = " + f(x) + NL + NL + ">> x = " # 取得f(x)之值 ok end
eval("for i in 1:10 see i*i+NL next")
系統函數
Ring內建的系統函數。陣列(List)
List是Ring中的基本資料型態,之前已有用過,在此再做點介紹。建立list。複習一下,我們已經知道可以使用以下方法建立list。
也可以使用list()函數來建立陣列
List1 = [1,2,3,4,5] List2 = 1:10 List3 = "z":"A" ? list1+NL+list2+NL+list3 # 大小寫同型
list1 = list(5) # 長度為5的list ? list1 # 元素預設值為0 list2d = list(2,3) # 2D陣列 ? list2d
list內的元素存取其實沒甚麼好說的,主要要注意的是在Ring中,index是由1開始起算的。
List1 = 1:10 # 建立一個list see List1[1]+NL # 使用index取得對應的值(Note: Ring的index自1開始) List1[1] = 99 # 改變list中某位置的值 see List1
加入元素。
List1 = 1:5 add(list1, 6) # 使用add()函數加入元素 List1 + 7 # 如前所述,也可使用+號直接加入元素 ? list1 insert(list1, 0, 100) # 在第0位置加入元素100,將成為list中第一個元素 ? list1 insert(list1, len(list1), 99) # 在最後一位加入元素99,Len()傳回list長度 ? list1
- len()可用來求得list長度。
- insert(list, pos, ele)若pos為len(list)則相當於add(list, ele)。簡單的種樹問題,兩棵樹有三個空缺,空缺的編號(pos)正好為0~len(list)。
刪除元素。
沒甚麼好說的,就是使用del()函數。若是要刪除list內所有元素,可以如下:
List1 = "a":"f" del(list1, 3) # 刪除index=3位置的元素,之後元素須往前補 ? list1 ? "list1[3] = " + list1[3]
事實上更簡單的方式為:
List1 = "a":"f" ? "list1 = " ? list1 k = len(list1) # 先取得list1的長度 for i=1 to k del(list1, 1) next ? "list1 = " ? list1
list1 = []
搜尋list。使用find()或BinarySearch()函數。
ab = [] for L in "a":"z" step 3 ab + L next ? ab ? find(ab, "j") # 傳回欲搜尋元素之index ? Binarysearch(ab, "p") # 使用BinarySearch,傳回欲搜尋元素之index ab2 = [] i = 1 for L in "a":"z" step 3 ab2 + [i, L] i++ next ? ab2 ? find(ab2, "j", 2) # 搜尋每個元素的第2欄(第2個子元素),傳回該元素所在之index ? Binarysearch(ab2, "p", 2) # 使用BinarySearch,傳回欲搜尋元素之index
Reverse & Sort。
list1 = 1:5 list1 = reverse(list1) # 將list1倒置 ? list1 list1 = sort(list1) # 將list1由小到大排序 ? list1 ? "============================" ab2 = [] for L in "a":"z" step 3 ab2 + [random(26), L] next ? ab2 ? "----------------------------" ab2 = reverse(ab2) # 將ab2倒置 ? ab2 ? "----------------------------" ab2 = sort(ab2, 1) # 根據第1個欄位排序(改為2則根據第二個欄位排序) ? ab2
Access List Items by String Index: 之前提過的,原則上就是dict in Python。
alist = [:one=1, :two=2, :three=3] ? alist[:two] alist[:four]=4 # 加上新元素 ? alist # traversal for ele in alist ? "alist["+ele[1]+"] = " + ele[2] end # 原來的長相如下: blist = [["one", 1],["two", 2],["three", 3]] ? blist["one"] blist["four"]=4 # 加上新元素 ? blist # traversal for ele in blist ? "blist["+ele[1]+"] = " + ele[2] end
字串(String)
這裡介紹一些關於字串的操作。建立字串。用""、''、``都可以。
s1 = "String1" s2 = 'String2' s3 = :String3 s4 = "do 're' mi" s5 = `"do" 're' "mi"` ? s1+NL+s2+NL+s3+NL+s4+NL+s5
len()、lower()、upper()、取得修改字母:原則上與list操作相同
s1 = "String1" s1 = lower(s1) # 轉換成小寫 ? s1 s1 = upper(s1) # 轉換成大寫 ? s1 for i=1 to len(s1) step 2 s1[i] = lower(s1[i]) # 取得修改字串的值 next ? s1
substring: trim()、left()、right()、substr()
s1 = " This is a String. " lefts = left(trim(s1), 4) # 先使用trim()去除s1兩邊的空白 ? lefts rights = right(trim(s1), 7) ? rights ## 使用substr() ? substr(s1, "is") // 取得is出現的位置,6,若不包含該子字串則傳回0 ? substr(s1, 6) // 取得s1中自6開始到最後的子字串(is is a String. ) >> 取中間到最後一段 ? substr(s1, 6, 5) // 取得自第6位開始後5個字元的子字串(is is) >> 取中間一段。原則上沒給5等於上一例,預設值為len(s1)-6 ? substr(s1, "is", "are") // 將s1中的"is"字串用"are"取代( Thare are a String. )
copy()、lines()
s1 = "One More Night" ? lines(s1) // 算出字串有幾行 >> 3 lines ? copy(s1, 3) // 複製字串3次 ? lines(copy(s1,3)) // 7 lines
strcmp():比較兩個字串
# 比較字串是否相同,相同傳回0,前比後大傳回1,反之傳回-1>> 0, 1, -1 ? strcmp("do", "do")+NL+strcmp("re", "mi")+NL+strcmp("fa", "sol")
str2list()、list2str(): 字串與list轉換。注意str2list產生的list中的元素為原str中的一行(非一個字元)。
str = "whatever" slist = str2list(str) add(slist, "do") # 使用list函數add()來加入新字串 ? slist ? "Elements in slist = " + len(slist) newstr = list2str(slist) ? "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 |
日期時間
直接練習下例。
? time() # 顯示目前時間 ? Date() # 顯示今日日期 ? addDays(date(), 1) # 顯示明天(+1天) ? addDays(date(), -1) # 昨天 birthday = "10/10/2021" ? "到今年雙十還有"+diffdays(birthday, date())+"天" # 兩個日期的差 ? clockspersecond() # 1000, 每一秒1000ticks(毫秒) t1 = clock() # 自開始程式到目前經過多少ticks(毫秒) ? "t1 = " + t1 // # 暫停兩秒 while clock() - t1 <= 2*clockspersecond() end duration = clock() - t1 # 從t1到目前時間(ticks/1000=秒數) ? "duration = " + duration tlist = timelist() # 包含所有時間資訊的list for i=1 to len(tlist) ? ""+i+" "+tlist[i] next # 設計另一個list表示對應位置的資訊(可僅列我們所需要的), 例如: ti = [:week=2, :month=4, :datetime=5, :day=6, :hour=7, :minute=11, :second=13, :date=16, :time=17, :year=18] # 可以自己組合需要的時間資訊 ? timelist()[ti[:date]] ? 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種寫法:
new Node { # 產生一個無名的物件 x = 10 y = 20 print() } n2 = new Node # 產生另一個物件 n2.id = 2 n2.x = 30 n2.print() class Node id=1 x y=60 # attribute,變數間用空白隔開,可給預設值 func print() # method see "id=" + id + " x=" + x + " y=" + y + NL
第II種寫法:
new Vertex(3,30,20) # 直接呼叫init()產生一無名物件 v2 = new Vertex(5, 50, 60) # 建立另一個物件 class Vertex id x y # attribute,變數間用空白隔開,可給預設值 func init a, b, c id = a x = b y = c print() func print see "id=" + id + " x=" + x + " y=" + y + NL
第III種寫法:
p = new point([:id = 8, :x = 11, :y = 12]) # 傳入一個list p.print() class Point id x y func init par # 使用list作為傳入值 id = par[:id] x = par[:x] y = par[:y] func print() see "id=" + id + " x=" + x + " y=" + y + NL
範例:包含使用setters&getters,private attributes&methods,composition(使用另一個物件作為一個物件的attribute)
v = new Vertex(5, 50, 60) # 建立一個vertex物件 ## test the vertex ? v.toString() # print out vertex info v.x = 100 # 直接修改x的值,因為會呼叫setx() v.y = 200 # 直接修改y的值,因為會呼叫sety() ? "x = " + v.x + " y = " + v.y + NL # 直接得到x與y的值,因為會呼叫getx()、gety() ? "id = " + v.getid() // v.setId(18) # Error! 自外部呼叫private method u = new Vertex(7, 11, 22) # 建立另一個vertex物件 ## about the arc arc = new Arc(u, v) # Arc的引數為兩個vertex物件 ? arc.toString() # print out arc info class Vertex x y # attribute,變數間用空白隔開,可給預設值 func init a, b, c id = a # id 是private variable x = b y = c # setters & getters func setX newX x = newX func getX return x func setY newY y = newY func getY return y func getId return id func toString() return "id=" + id + " x=" + x + " y=" + y + NL # 以下區塊為 private attributes & methods private id func setId newId id = newId class Arc va vb func init a, b va = a vb = b func length() return sqrt((va.x-vb.x)*(va.x-vb.x)+(va.y-vb.y)*(va.y-vb.y)) func toString() return va.toString()+NL+vb.toString()+NL+"Length = " + length()
override operator這個method來定義符號,需要兩個物件作為輸入參數。下例僅供練習,因為+、-、*、/會傳回新的物件,而>與<則會傳回boolean,不是一個很好的設計。
one = new onething(10,20) # 建立一個物件 two = new onething(100, 200) # 建立另一個物件 three = one + two # 使用operator建立另一個物件 ? three ? one > two # 使用>或<判斷兩物件大小 class Onething a b func init m,n a = m b = n func operator op, anotherthing result = new Onething switch op on "+" result.a = a+anotherthing.a result.b = b+anotherthing.b on "-" result.a = a-anotherthing.a result.b = b-anotherthing.b on "*" result.a = a*anotherthing.a result.b = b*anotherthing.b on "/" result.a = a/anotherthing.a result.b = b/anotherthing.b on ">" if a > anotherthing.a ? ""+a + ">" + anotherthing.a return true else ? ""+a + "<" + anotherthing.a return false ok on "<" if a < anotherthing.a ? ""+a + "<" + anotherthing.a return true else ? ""+a + ">" + anotherthing.a return false ok off return result func toString() return ""+a+" "+b
Inheritance:繼承使用關鍵字from
Peter = new Spiderman{ # new一個物件 name = "Peter Parker" age = 17 strength = 86 skills = ["Accelerated healing", "Precognitive", "Utilizing wrist-mounted web-shooters"] } ? peter.toString() # new另一個物件(Hero部分的變數使用預設值) peter2 = new spiderman(["Accelerated healing", "Precognitive", "Utilizing wrist-mounted web-shooters"]) # 修改預設值 peter2.name = "peter2" ? peter2.toString() class Hero # 一個名為Hero的物件 name="Peter Parker" age=17 strength = 80 func init n,a, stren name = n age = a strength = stren func toString() return "Name = " + name + " Age = " + age + " Strength = " + strength + NL class Spiderman from Hero # 繼承自Hero的物件 skills func init skillsList skills = skillsList func toString() skill = "" count = 1 for s in skills skill = skill + count + ". " + s + NL count++ next return super.toString() + skill
Package: 可以將所有的class放在一個package內,只要使用package關鍵字並定義名稱即可。如此可以較容易收納。好像無法直接在package內定義函數,不過我們可以如例中將函數收納在一個class內,然後使用時再呼叫。
每次要使用package內的class,都要在之前加上package的名稱,為了簡化,可以先使用import將package導入,然後便可直接使用了。以下修改上例中package之前的程式碼。
v1 = new graph.vertex(1,2,3) v2 = new graph.vertex(7,8,9) ? v1.toString() + v2.toString() a1 = new graph.arc(v1,v2) ? "a1.length = " + a1.length f = new graph.funs # 建立funs物件然後呼叫相關函數 f.hello("Mary") ? f.mul(10,20) package graph class Funs # 放置需要的函數之物件 func Hello(name) see "Hi, " + name + NL func mul(a,b) return a*b class Vertex id x y # attribute,變數間用空白隔開,可給預設值 func init a, b, c id = a x = b y = c # setters & getters func setX newX x = newX func getX return x func setY newY y = newY func getY return y func toString() return "id=" + id + " x=" + x + " y=" + y + NL # private attributes & methods private func setId newId id = newId class Arc va vb length func init a, b va = a vb = b length = length() func length() return sqrt((va.x-vb.x)*(va.x-vb.x)+(va.y-vb.y)*(va.y-vb.y)) func toString() return va.toString()+NL+vb.toString()+NL+"Length = " + length()
簡化了許多。
import graph v1 = new Vertex(1,2,3) v2 = new Vertex(7,8,9) ? v1.toString() + v2.toString() a1 = new arc(v1,v2) ? "a1.length = " + a1.length f = new funs # 建立funs物件然後呼叫相關函數 f.hello("Mary") ? f.mul(10,20)
當我們要處理好幾個instances時,可以將這些instances放在一個list(collection)內,因為可能要對於這個list做一些操作,所以做法可以在建立一個class,讓此list作為物件變數,然後建立物件方法來操作它(也可參考java或python之資料結構做法)。此外,此例中也介紹了sort()與find()函數。
names = ["Spider man", "Iron man", "Super man", "Bat man", "Captain America"] ages = [:spiderman=17, :ironman=41, :superman=27, :batman=33, :captain=90] herosList = [] for i=1 to len(names) add(herosList, new hero {name = names[i] age = ages[i][2]}) next _heros = new heros(herosList) # create a new instance containing heros see _heros.toString() see _heros.sortby(:age) # or "age" ? _heros.searchfor(27, :age) # search for one age = 27 class hero name age def toString() return name + " " + age class heros hsList = [] def init(hs) hsList = hs def getHeros() return hsList def sortBy(att) # 使用函數sort(List:list to sort, nColumn:用於多維, cAttribute:屬性)來排序 return sort(hsList, 1, att) func searchfor(value, attribute) # 使用函數find(List:list to sort, ItemValue:值, nColumn:用於多維, cAttribute:屬性)來搜尋 return find(hsList, value, 1, attribute) def toString() s = "" for h in hsList s += h.toString() + NL next return s
self & this
self主要是要避免跟globle variable的名稱衝突,事實上並不見得需要使用。那麼this呢?
name = "Tony Stark" # Global variable age = 41 # Global variable spiderman = new hero("peter parker", 17) see spiderman ? spiderman.name+" "+spiderman.age spiderman.setname("Jonny") ## 無法使用spiderman.namd = "Jonny" ? spiderman.name class hero self.name self.age ## 使用self修飾避免與global variable衝突 func init(aname, newage) self.name = aname self.age = newage func setname(aname) self.name = aname func getname() return self.name func setage(newage) self.age = newage func getage() return self.age
也可以用this替代self。
spiderman = new hero{name = "peter parker" age=18} ? spiderman.info() class hero name age attackpower = 80 def info ? "name = " + self.name + " age = " + age + "attack power = " + attackpower + NL w = new weapon{ ? "name = " + self.name # weapon 裡面的self表示weapon這個class ? "owner = " + this.name # this 表示hero這個class ? "attack power" + this.attackpower } class weapon name = "web shooter" attackpower = 100 owner
Note: 原則上Ring的語法不太需要使用self&this。
spiderman = new hero{name="Peter Parker" age = 17} ? spiderman ? spiderman.toString() class hero this.name this.age func toString() return name + " " + age
IO
簡單的輸出入例如see、give、put、get等不再贅述。使用load:把需要的函數及class寫在一個檔案內(e.g. fun.ring),在主程式使用load關鍵字來導入即可使用。例如:
如果fun.ring內的classes是定義在一個package內(e.g. package fun.classes),那再使用時可以在load之後import這個package即可使用。
load "fun.ring" # 在同一資料夾有一fun.ring檔案 hello("Mary") # fun.ring內的函數 v1 = new vertex(1,2,3) # fun.ring內的class v2 = new vertex(2,10,20) v1.x = 11 v1.y = 12 ? v1.toString() a = new arc(v1, v2) # fun.ring內的class ? a.toString()
我們可以load超過一個檔案,不過當兩個檔案中有同名的函數(或物件),則會產生錯誤。不像其他語言(例如python)可以定義Name space,使用Ring時設計名稱不要重複即可。
load "fun.ring" # 在同一資料夾有一fun.ring檔案 import fun.classes
Files:此處簡單介紹一些常見的檔案操作,其他詳細的函數列表請參考官網。
-
在Ring中檔案的操作,最簡單的讀寫可以很容易地達成,如下:
- write("abc.txt", "A string to write.") # write(filename, string)直接寫入檔案,若無該檔案會自動產生
- see read("abc.txt") # read(filename)直接讀取內容(string)出來,可以讀取binary file(e.g. read("fun.ring")
- 直接使用write()雖然方便,不過若是要append便無法控制,所以也可以使用如下方式寫入:
- # 先使用fopen()函數開啟檔案資料流,第二個參數可以選擇r, w, a, r+(r/w), w+(r/w), a+(r/a)
- f = fopen("abc.txt", "a")
- fwrite(f, NL+"something new") # 使用fwrite()寫入
- fclose(f) # 使用fclose()關閉檔案資料流
- 可以使用fread(file handle, nSize)來讀取檔案內容,可以控制讀取的大小(nSize)。
- f = fopen("abc.txt", "r")
- r = fread(f, 100) # 讀取內容,第二個參數是size
- see r+NL
- fclose(f)
- 如果要得到逐行的資料作進一步分析,可以先使用read()讀進,再使用str2list()函數取得各行資料即可。
- if fexists("abc.txt") ## 確認該檔案確實存在
- s = read("abc.txt") # 讀取檔案所有資料
- alist = str2list(s) # 將字串轉為list
- ? len(alist) # 行數
- ? alist[1] # 第一行資料
- else
- ? "abc.txt is not existed."
- ok
- 一些關於檔案的操作:
- d = "E:\your\target\directory" # 你的目標資料夾路徑
- if direxists(d) # 檢查此資料夾是否存在
- filelist=dir(d) # 取得資料夾內所有檔案及資料夾資訊
- else
- ? "No such directory"
- ok
- for x in filelist # x包含兩個資訊,x[1]>檔案名,x[2]>型態(0=file, 1=folder)
- if x[2] # 如果x[2]為1(true),表示是folder
- see "Directory: " + x[1] + NL
- else # 表示是檔案
- see "File: " + x[1] + NL
- ok
- next
- see "Files = " + len(mylist)
- # rename(oldFileName, newFileName)
- rename("abc.txt","xyz.txt") # 將檔案改名
- # remove(delete) file
- remove("xyz.txt") # 刪除檔案