#

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計算機

當使用者輸入完運算式後按下右邊的Calculate就可以馬上算出結果,然後顯示在同一個文字輸入方塊上,如圖5-2:



圖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

留下您的意見

Theme Design by devolux.org. Converted by Wordpress To Blogger for WP Blogger Themes. Sponsored by iBlogtoBlog
This template is brought to you by : allblogtools.com | Blogger Templates