Tcl的直譯器在處理每一個命令時都會執行變數、子命令及反斜線代換等動作,但不會處理數學的運算,換句話說像puts 1+2的命令,其輸出是1+2而不是想像的3,如果真的要執行運算則需要靠expr命令來做到。這篇文章簡單的說明expr的使用方法。
5.1 expr 運算命令
expr命令是Tcl裡專門用來做運算的命令,一般的情況我們透過expr及子命令代換來達成運算的需求,如下是expr命令的語法:expr 參數1 [參數2] ... [參數N]
expr會把參數1~參數N串接起來當成是一個數學運算式來看,並求出運算式的值。以下是使用範例:
請注意!第3行和第4行的結果是不同的,如果expr裡的運算元都是整數,輸出結果會無條件捨去小數部份。expr裡的運算元允許變數及子命令代換。
5.2 expr提供的函數
上一個程式的第5行使用了sin函數,當然除了sin之外expr還提供了其它的函數,以下是expr提供的其它函數列表,別刻意去記它們,有用到再查就好。abs(arg) | 對arg求絕對值 |
acos(arg) | 三角函數的 arc consine,回傳值範圍 [0,pi] |
asin(arg) | 三角函數的arc sine,回傳值範圍 [-pi/2,pi/2] |
atan(arg) | 三角函數的 arc tangent,回傳值範圍 [-pi/2,pi/2] |
atan2(y, x) | 三角函數的 arc tangent(y/x),回傳值範圍[-pi,pi] |
ceil(arg) | 求大於arg的最小浮點數值(小數部份是0) |
cos(arg) | 三角函數的consine |
cosh(arg) | 三角函數的 hyperbolic cosine |
double(arg) | 把arg轉型成double型態回傳 |
exp(arg) | 對arg求指數exponential |
floor(arg) | 求小於arg的最大浮點數 |
fmod(x, y) | 以浮點數型態回傳x/y的餘數 |
hypot(x, y) | Computes the length of the hypotenuse of a right-angled triangle sqrt(x*x+y*y) |
int(arg) | 傳回arg無條件捨去後的結果. |
log(arg) | 對arg求natural logarithm |
log10(arg) | 對arg求以10為基底的logarithm |
pow(x, y) | 求x的y次方 |
rand() | 傳回一個pseudo-random floating-point value in the range (0,1) |
round(arg) | 傳回arg四捨五入後的結果 |
sin(arg) | 三角函數的arc sine |
sinh(arg) | 三角函數的 hyperbolic sine |
sqrt(arg) | 對arg開根號 |
srand(arg) | 使用arg來重置亂數產生器,並回傳第一個產生的亂數,arg必需是整數. |
tan(arg) | 三角函數的 arc tangent |
tanh(arg) | 三角函數的 hyperbolic tangent |
wide(arg) | 轉換arg為最少64位元的整數 |
5.3 expr位元運算
除了上述的數學函數外,expr也可以做位元運算,以下是位元運算子的列表:~ | 位元的NOT運算 |
<< | 位元左移 |
>> | 位元右移 |
& | 位元AND運算 |
^ | 位元XOR運算 |
| | 位元OR運算 |
以下是使用範例:
5.4 expr邏輯運算
Tc可以做數值比較,字串也可以拿來比,不管是數值或字串的比較,只要邏輯成立expr就傳回1,否則傳回0。> | 大於,可用於數值和字串 |
< | 小於,可用於數值和字串 |
>= | 大於等於,可用於數值和字串 |
<= | 小於等於,可用於數值和字串 |
== | 等於,可用於數值和字串 |
!= | 不等於,可用於數值和字串 |
eq | 等於,字串專用(逐字元比較) |
ne | 不等於,字串專用(逐字元比較) |
! | 邏輯的NOT運算真變假、假變真 |
|| | 邏輯的OR運算 |
&& | 邏輯的AND運算 |
以下是使用範例:
5.5 expr計算機製作
接下來我們要使用expr命令做一個簡單的計算機,程式執行後可以讓使用者在文字輸入方塊上填入數學運算式如圖5-1:
圖5-1 expr計算機
圖5-2 expr計算機執行結果
這個程式的觀念非常的簡單。程式的第1行很單純的建立了一個文字標籤Expression。第2行再建立一個文字方塊並指定輸入的內容要儲存在全域的::txt變數裡。第3行建立一個顯示為Calculate的按鈕,當按鈕按下後會取出::txt變數的值並用expr命令運算,運算的結果再存回::txt變數。第4行透過pack命令把文字標籤、文字方塊及按鈕逐一排列在視窗上,-side用來指定排列的方法,這裡指定由left(左)到右排列。
本章回顧
Tcl的直譯器在處理每一個命令時都會執行代換的動作,但不會處理數學的運算,例如:puts 1+2輸出是1+2而不是想像的3。expr命令是Tcl裡專門用來做運算的命令,透過子命令代換就可以達成運算的目的,語法如下:
expr 參數1 [參數2] ... [參數N]
expr會把參數1~參數N串接起來當成是一個數學運算式來看,並求出運算式的值。例如:
這樣1+2的結果就會被代換出來。
按右上方的「#」號切換側邊欄
29 個意見
匿名 | 2011年8月2日 下午4:11
你好 請問TCL有內建取max和min的函數嗎? 謝謝
dai | 2011年8月7日 下午1:45
有的~~ 請參考下面的連結
http://docs.activestate.com/activetcl/8.5/tcl/TclCmd/mathfunc.htm
匿名 | 2011年11月27日 上午11:13
看到這裡
你的網頁教學,實在是太讚,讚到不行,不得不頂!
dai | 2011年11月27日 晚上9:37
哈 ~ 謝謝誇獎~希望你學習順利!!
匿名 | 2011年11月30日 凌晨12:56
dai
您教學網頁確實很簡明,很讚的。
我有e-mail給您,目前急需,如何將C++的編程功能,加入到TCL網頁程式裏面 ?
也就是C++與TCL聯合編程。需要哪些工具?如何操作? 希望您,寫一個短篇的教材。
我的msn:crazystudent.tw@yahoo.com.tw
是電腦程式業餘同好,希望可以多談!交流心得!
dai | 2011年11月30日 晚上7:52
@@ 我沒有收到你寄的信也!!
匿名 | 2011年11月30日 晚上8:02
沒收到信沒關係啊。我收到你msn加入邀請了
C++與 TCL 怎樣對接呢? 怎樣讓TCL順利呼叫C++來做事?麻煩解答一下喔
dai | 2011年11月30日 晚上8:25
把c++編成Tcl可以載入的dll或so檔就可以了,我學弟有寫一篇文件,你可以先參考看看
http://jky777.got7.org/2009/11/swig-tcl-c.html
匿名 | 2011年11月30日 晚上8:32
太感謝dai兄
msn加入了,有空來聊一下啊
匿名 | 2011年11月30日 晚上10:17
dai兄
不好意思,看了你學弟的文章,很不錯,但可能我功力太淺,還是沒成功
1.首先,我依照您的說法,先用makefile 直接編譯出 hello.so檔 (沒有經過SWIG 與 hello_wrap.c的過程)
然後 load hello.so
直接執行 hello
出現:Error in startup script: couldn't find procedure Hello_Init
while executing
"load hello.so "
(file "hello.tcl" line 1)
2.依照您學弟的作法,在他的第3步驟,$gcc -fpic -shared -static hello.c hello_wrap.c -I/opt/ActiveTcl-8.6/include -o hello.so
我的問題在 -I/opt.....找tcl.h 但我用電腦搜尋,找不到tcl.h...我的也是linux系統!>.< 頭痛啊!
您能夠msn聊嗎? 太感謝您這樣熱心的網友啦!
dai | 2011年11月30日 晚上10:43
不然下載個 Tcl-8.6 的 source 然後用裡面的tcl.h試試~
匿名 | 2011年11月30日 晚上10:57
OK 謝謝 有空msn聊
匿名 | 2011年12月1日 下午6:26
dai兄
很慚愧,SWIG的方式,弄不成功,下載了TCL8.6也沒找到tcl.h
您有tcl.h檔的話請e-mail給我,就是先前留給你的msn帳號
感謝
匿名 | 2011年12月1日 晚上9:24
不好意思dai兄!
我再網路上找到tcl.h檔案了
但是按照您學弟方式編程
出現hello_wrap.c:780: error
出現很多行這種錯誤也!!
hello_wrap.c 這不是 $swig -tcl hello.i 這個指令所產生的嘛??怎麼會出現錯誤無法編程?
一關還有一關,程式人生 真是好坎珂>.<!!
dai | 2011年12月1日 晚上11:42
有更多的錯誤訊息可以參考嗎?? 看能不能幫上一點忙~
匿名 | 2011年12月2日 凌晨12:14
再下將所有錯誤訊息e-mail給您了
您能上msn嗎?
不好意司再這灌版面,可以e-mail與msn談比較妥
匿名 | 2011年12月2日 上午10:55
改用g++去編同樣的檔案
錯誤較少
但還是一樣有錯。結果出現這個:
exam_wrap.c:780: error: ‘Tcl_Obj’ has not been declared
exam_wrap.c: In function ‘void SWIG_Tcl_SetErrorObj(Tcl_Interp*, const char*, int*)’:
exam_wrap.c:783: error: ‘Tcl_SetObjResult’ was not declared in this scope
exam_wrap.c: In function ‘void SWIG_Tcl_AddErrorMsg(Tcl_Interp*, const char*)’:
exam_wrap.c:803: error: invalid conversion from ‘const char*’ to ‘char*’
exam_wrap.c:803: error: initializing argument 2 of ‘void Tcl_AddErrorInfo(Tcl_Interp*, char*)’
exam_wrap.c: At global scope:
exam_wrap.c:833: error: ‘Tcl_Obj’ has not been declared
exam_wrap.c:834: error: ‘Tcl_Obj’ has not been declared
exam_wrap.c:862: error: ISO C++ forbids declaration of ‘Tcl_Obj’ with no type
exam_wrap.c:862: error: expected ‘;’ before ‘*’ token
exam_wrap.c:866: error: ‘Tcl_Command’ does not name a type
exam_wrap.c:872: error: ‘Tcl_Obj’ has not been declared
exam_wrap.c:893: error: ‘Tcl_Obj’ has not been declared
exam_wrap.c: In function ‘void SWIG_Tcl_SetConstantObj(Tcl_Interp*, const char*, int*)’:
exam_wrap.c:895: error: ‘Tcl_NewStringObj’ was not declared in this scope
exam_wrap.c:895: error: ‘Tcl_ObjSetVar2’ was not declared in this scope
exam_wrap.c:896: error: invalid conversion from ‘const char*’ to ‘char*’
exam_wrap.c: At global scope:
exam_wrap.c:899: error: expected initializer before ‘*’ token
exam_wrap.c:2219: error: expected ‘}’ at end of input
dai | 2011年12月2日 上午11:09
看起來這個問題是tcl.h或是某些.h檔找不到引起的問題 ~
正常情況下,如果你是裝ActiveTcl 只要在編輯的時候指定.h檔的尋找路徑像下面
-I/opt/ActiveTcl-XX/include
應該就可以解決了。
ps. ActiveTcl 裝下去應該會有tcl.h檔才對 ~~
我最近事情比較多點 ~ 可能比較沒空MSN!! 要看運氣了 哈 ~
dai
匿名 | 2011年12月2日 下午1:12
OK....下載ActiveTcl8.6 去試看看
有空也可e-mail。我目前接很多單幫電腦案子,所以要自己學。把C++用上去,非常重要。
順便問一下JAVA 也跟TCL一樣有視窗能力嗎? 好像跟C語言很相似。直接用C就好了。
匿名 | 2011年12月2日 下午1:50
報告dai兄
重新下載果然成功了!先前拼湊的tcl.h檔案果然不能!
太感謝您的提示,成功了 >.<真不容易
不過很開心!呵。
dai | 2011年12月3日 中午12:53
呵 ~ 雖然有點曲折 ~ 不過還是恭喜你成功了!!
JZ | 2012年4月6日 晚上9:10
Dai大大,請你看看這段程式碼,我一直想不透= =...
set width 16.56
set dis 5
set start 0
for {set b 1} {$b <= 16} {incr b} {
set xi($b) [expr [expr $b+4-1]%4]
set xpimp1($b) [expr $xi($b)*($width+$dis)+$start]
}
那執行的結果,得到xpimp1(1)為0.0、xpimp1(2)為21.56、xpimp1(3)為43.12、xpimp1(4)為64.67999999999999,
而我的問題在於,為何在xpimp1(1)與xpimp1(4)中會得到這樣的結果,跟我預期的0與64.68不符,我應該做怎樣的調整,謝謝Dai大大 :D
dai | 2012年4月7日 凌晨1:08
在很多的程式語言裡,如果運算式中任一運算元出現浮點數,那結果就會是浮點數,Tcl也採用這樣的方式
所以你的xpimp1(1)會是0.0,如果要強制變為整數可以用 expr int(1.6) 或 expr round(1.6)。
另外xpimp1(4)出現這麼多9也是很多程式語言會出現的問題,這是由於電腦採用2進制儲存浮點數的關係,所以會有一些誤差。
你可以用 format 命令來處理這個問題 ex.
format %.4f 1.23456788
Unknown | 2014年5月3日 下午3:19
Dai大大 ,請問有啥函數,能夠將數值計算以0.005為單位方式進位?
例如: 0.003 會進位成0.005 , 0.008 進位成0.01
之前我是用判斷的的方式來進位,但寫出來怪怪的
下面是我大概寫的方式,b為要判斷的值,
將小數第三位變成整數,去判斷後再看是否進位成0.005
然後在+回來
set tcl_precision 3
set b 0.027
set a [expr [expr $b *100 - int($b*100) ] *10 ]
if { $a == 0} {
set b [expr [expr 0 + int($b*100) ] /100 ]
} elseif { $a == 1 } {
set b [expr [expr 0 + int($b*100) ] /100 ]
} elseif { $a == 2 } {
set b [expr [expr 0 + int($b*100) ] /100 ]
} elseif { $a == 3 } {
set b [expr [expr 0.5 + int($b*100) ] /100 ]
.
.
.
} elseif { $a == 9 } {
set b [expr [expr 1 + int($b*100) ] /100 ]
THx
匿名 | 2014年5月3日 晚上9:09
記得沒有現成的函數,但可以這樣:
set p 3
set b 0.027
puts [expr $b + ((10 - (int($b*pow(10, $p))%10) )%5 + 0.0) / pow(10, $p)]
Unknown | 2014年5月3日 晚上11:19
謝謝 Dai大大 我來try 看看
超感謝的 想破頭了~
匿名 | 2015年6月18日 下午2:28
Dai大大, 請問 tcl 有沒有 16 進制的加減法運算,
例如: 7+4 = B, 10-4 = C
感謝
Unknown | 2015年7月9日 晚上7:02
123123
Unknown | 2015年7月9日 晚上7:20
sss
留下您的意見