1. 程式人生 > >Lisp語言:變數以及變數的作用範圍

Lisp語言:變數以及變數的作用範圍

到現在才討論變數似乎有點遲,在過去的文章中我們已經很多次使用變數。

之所以到現在才討論變數是因為不想各位因為複雜的變數使用規定感到困惑。如果只是基本使用,理解變數確實可以很簡單。就像在之前的幾篇文章中,我們使用了很多變數,我們不用進行復雜的討論也可以理解這些變數。變數的簡單使用也就是如何賦值,如何取值。

前面的文章我們使用setq函式對變數賦值,使用時直接通過變數名代表對應的值。如下面這樣的程式碼:

(setq my-value 10)

(format *query-io* “value of my-value is : ~a” my-value)

不過,當我們進一步深入討論函式,函式的引數,甚至是函式的遞迴呼叫時,就必須開始展開有關變數的討論了。

變數名

首先是有關變數名的規定。不同的語言對變數名的使用都有一定的規定,如不能使用括號、分號,不能使用if, while這樣的關鍵字等等。一種語言作出這種規定是必然的,任由程式設計師使用任意字元作為變數名會導致程式變得不可解釋。不過,對於程式設計師來講,大可不必詳盡地記憶所有規定,簡單的做法是儘量保守地使用變數名,如單純地使用字母組合,像accountname,templist等。值得注意的是,Common Lisp語言允許程式設計師可以在變數名中使用橫線“-”,而且橫線的使用被當做Lisp語言的一種風格。所以,如果你想讓人覺的你是個Lisp高手,建議使用account-name,temp-list這樣的變數名。

如之前的文章提到過的,CLisp環境中允許使用中文字元作為變數名,如“我的變數”這樣也是一個合法的變數名。不過,由於中文的特殊性,個人建議不要使用中文字元作為變數名。

還有就是Common Lisp中的全域性變數一般以*號開頭,以*號結束,比如我們之前呼叫format函式時使用的*query-io*就是系統定義的一個全域性變數。如果你自己有一些全域性變數的話,最好也是使用相同的格式,如*my-database*。

變數型別

有關變數型別的一個好訊息是Lisp中的變數不需要指定型別,對於一個變數,賦予它什麼型別的值它就是什麼型別的變數。如(setq i 10),那i就是一個數字型別,而(setq i "my string")會讓i變成一個字串型別的變數。Lisp語言會在變數使用過程中動態檢查他們的型別,比如(setq i "my string") (+ i 10)會報錯,Lisp在執行“(+ i 10)”的過程中發現變數i是一個字串,不能進行“加”的操作,所以報錯。

對於那些使用JavaScript, Ruby等動態型別語言的程式設計師來講,動態型別是一個熟悉的概念。而對於那些習慣於c, c++ , java中靜態型別的程式設計師來講,Lisp中的這種動態型別變數會有點令人不舒服,感覺語言的不確定性很大,不好把握。不過,動態型別正是Lisp語言的一大優勢,對於動態變數的優勢我們在以後的文章中繼續討論。目前我們暫時簡單地記住Lisp中的變數是動態型別就好了。

變數的作用範圍

相比變數名的規定,變數的作用範圍對程式設計師來講更加重要一些。因為變數名和變數的型別的錯誤使用比較容易被系統發現,程式設計師更容易發現問題,解決問題。而系統並不容易識別變數作用範圍的錯誤,如果對變數的作用範圍理解不準確,在程式中使用不當,往往程式執行時不報錯,但是執行結果是錯誤的。

在沒有任何附加條件下,CLisp中通過“setq”或者是“setf”函式設定的變數是全域性變數。其中提到的setf和setq都是設定變數的函式,有關他們的區別我們後面有機會再討論,現在暫時認為它們是一樣的。

也就是說你在函式外面定義一個變數,在函式中可以直接使用。反過來,如果你在函式內定義一個變數,在函式外面也是可以使用的。

如下面的程式碼,function1函式外定義的變數var-i可以在function1中直接使用,而function1中定義的變數var-j也可以在function1退出後使用:

(defun var-test ()
        (setf var-i 1)
        (format *query-io* "var-i is: ~a ~%" var-i)
        (function1)
        (format *query-io* "var-i is: ~a ~%" var-i)
        (format *query-io* "var-j is: ~a ~%" var-j))

(defun function1 ()
        (setf var-j 10)
        (format *query-io* "in function:var-i is: ~a ~%" var-i)
        (format *query-io* "in function:var-j is: ~a ~%" var-j))

以上程式碼執行結果如下:

 

當一個函式有定義引數時情況會變得複雜一點。

首先有一點是比較明確的,函式的引數只在函式內有效。比如我們定義了函式var-test,帶有引數var-f,那麼var-f可以作為一個變數在函式var-test中使用。不過,一旦離開函式var-test,var-f就不能使用了。

問題就是如果一個函式的引數和一個全域性變數同名會出現什麼情況。

如下面的程式碼,函式function2有一個引數var-j,而函式function2外定義了一個全域性變數var-j。

在這種情況下,var-j在function2函式內可以被當做一個臨時變數,function2中對var-j的任何修改都不會影響function2以外var-j的值。

(defun var-test2 ()
        (setf var-i 1)
        (setf var-j 2)
        (format *query-io* "var-i is: ~a ~%" var-i)
        (format *query-io* "var-j is: ~a ~%" var-j)
        (function2 var-j)
        (format *query-io* "var-i is: ~a ~%" var-i)
        (format *query-io* "var-j is: ~a ~%" var-j))



(defun function2 (var-j)
        (format *query-io* "in function:var-i is: ~a ~%" var-i)
        (format *query-io* "in function:var-j is: ~a ~%" var-j)
        (setf var-i 10)
        (setf var-j 20)
        (format *query-io* "in function:var-i is: ~a ~%" var-i)
        (format *query-io* "in function:var-j is: ~a ~%" var-j))

以上程式碼中var-j首先被賦值為2,函式function2定義了引數var-j,所以var-j在函式function2中相當於一個臨時變數,在函式function2中被賦值為20,一旦離開函式function2,它的值又變回2了。

另外一個變數var-i,因為沒有定義為函式function2的引數,所以它一直都是全域性變數,在函式function2被賦值為10,離開函式function2後值仍是10。


另外一種定義臨時變數的方法是使用let函式,let函式的使用例子如下:

(let ((x 5) (y 10)) 

            (format *query-io* "x is: ~a ~%" x)

            (format *query-io* "y is: ~a ~%" y)

            ;更多語句  

             )
上面的例子定義了一個作用範圍,在這個範圍中可以使用臨時變數x和y,對x和y的修改一旦離開let函式的範圍就失效了。

更詳細的樣例如下:

(defun var-test3 ()
        (setf var-i 1)
        (setf var-j 2)
        (format *query-io* "var-i is: ~a ~%" var-i)
        (format *query-io* "var-j is: ~a ~%" var-j)
        (let ((var-j 10))
                (format *query-io* "var-i in let is: ~a ~%" var-i)
                (format *query-io* "var-j in let is: ~a ~%" var-j)
                (setf var-i 100)
                (setf var-j 200)
                (format *query-io* "var-i in let is: ~a ~%" var-i)
                (format *query-io* "var-j in let is: ~a ~%" var-j))
        (format *query-io* "var-i is: ~a ~%" var-i)
        (format *query-io* "var-j is: ~a ~%" var-j))

函式var-test3中定義了兩個全域性變數var-i 和var-j,分別賦值為1和2

當進入let函式的範圍時,var-j被臨時賦予了10這個值,在這個範圍內var-j就是一個臨時變量了。接著var-j被賦值為200,不過一旦離開let的範圍,var-j的值就變回2了。

其中對全域性變數var-i的賦值操作會在全域性範圍生效,所以離開let的範圍後var-i的值還是100.



以上簡單講了變數的作用範圍,希望可以給你一些參考。

簡單的總結是:

1. setq, setf可以用於定義全域性變數。

2. 函式的引數是臨時變數,作用範圍只在函式內。

3. let函式定義的變數是臨時變數,在let函式內有效。

4. 對臨時變數執行setq和setf操作不對全域性變數產生影響。

其實,有關變數的作用範圍還有很多需要討論的,因為篇幅的關係,這裡暫時就不展開了。

最後要留意的是Common Lisp中有包的概念,引入包的概念以後對於變數的使用會更復雜一點。本篇文章的討論沒有考慮包的情況。

相關推薦

Lisp語言變數以及變數作用範圍

到現在才討論變數似乎有點遲,在過去的文章中我們已經很多次使用變數。之所以到現在才討論變數是因為不想各位因為複雜的變數使用規定感到困惑。如果只是基本使用,理解變數確實可以很簡單。就像在之前的幾篇文章中,我們使用了很多變數,我們不用進行復雜的討論也可以理解這些變數。變數的簡單使用

Lisp語言函式的定義,呼叫以及引數的傳遞

進一步瞭解一下Lisp語言中函式的使用,雖然從這篇文章才開始介紹函式,但是我們在之前的Lisp文章中已經多次接觸函數了。函式的定義在Lisp中函式的定義使用下面的格式:(defun function1 () (format *query-io* "in fun

小鳥初學Shell程式設計(七)變數引用及作用範圍

變數引用 那麼定義好變數,如何列印變數的值呢?舉例下變數引用的方式。 ${變數名}稱作為對變數的引用 echo ${變數名}檢視變數的值 ${變數名}在部分情況下可以省略成 $變數名 [root@lincoding ~]# string="hello Shell" [root@lincoding ~]#

js全域性變數和區域性變數以及變數宣告提升

區域性變數和全域性變數重名時: 一、Javascript的變數的scope是根據方法塊來劃分的(也就是說以function的一對大括號{ }來劃分)。切記,是function塊,而for、while、if塊並不是作用域的劃分標準。 二、 當全域性變數跟區域性變數重名時,區域性變數的scope

Lisp語言迴圈控制

瞭解了Lisp語言的條件判斷,讓我們看看Lisp語言的迴圈控制。對於稍有程式設計經驗的人來說,迴圈是比較容易理解的,一般語言裡面的迴圈無外乎for迴圈,while迴圈,do until迴圈等,有些語言的迴圈稍微複雜一點,也不過是一些變化形式。而Lisp語言裡的迴圈卻不是這麼簡

javascript中怎麼判斷某變數是null,undefined,還是不存這個變數 ? 以及變數是array 還是 object 還是 null ?

(以下用 i 變數做例子)看是否存在這個變數:  'i' in window  存在返回true反之返回false  (注意 i 要加上引號)是否為null: i == null  是則返回true反之返回false 是否為undefined: i == undefined

Lisp語言條件判斷

學習了Lisp語言的控制檯輸出與輸入,讓我們來看看Lisp語言的條件判斷。Lisp中的條件判斷看起來和其它語言差不多,學習起來還是比較簡單的。1. 基本用法:基本的用法像下面這個樣子:(if 條件判斷 條件成立時的結果

區域性變數和全域性變數以及作用範圍

       在寫工程檔案的時候,犯了一個基礎性的錯誤,基礎不牢,地動山搖。所以通過查閱資料回顧了一些相關知識,並記錄下來。防止以後再發生這種慘案。 變數按儲存區域分:全域性變數、靜態全域性變數和靜態區域性變數都存放在記憶體的靜態儲存區域,區域性變數存放在記憶體的棧區。

C語言常量以及變數型別,儲存型別和作用

變數 其值可以改變的量稱為變數。一個變數應該有一個名字,在記憶體中佔據一定的儲存單元。變數定義必須放在變數使用之前。一般放在函式體的開頭部分。要區分變數名和變數值是兩個不同的概念。 變數定義的一般形式為:    型別說明符  變數名, 變數名, ...;在書寫變數定義時,應注意以下幾點: 允許在一個型

【C語言簡單說】六取模運算子以及變數的擴充套件

┴┴ (╰(`□′)╯( ┴┴ … 這一節我們就來說另外的運算子——取模運算子(說白了跟取餘數差不多…<—_-)!!!) 先看看好難懂的定義:取模運算和取餘運算兩個概念有重疊的部分但又不完全一致。

2、C語言變數的儲存,生存週期,作用範圍分類

C語言中變數的儲存類別一:按作用域劃分1、區域性變數:自動區域性變數、靜態區域性變數、暫存器變數。(區域性可見性)2、全域性變數:靜態外部變數、外部變數。(全域性可見性)二:按生存周期劃分1、動態儲存:自動變數、暫存器變數、形式引數。(生存時間只限於其作用範圍)2、靜態儲存:靜態區域性變數、靜態外部變數、外

C語言小節篇1關於C語言結構體引用變數的問題

這個問題以前沒注意過,今天和同事討論後,查詢相關資料,想搞懂一個問題: 關於結構體引用變數的符號  .   和   ->   的區別。 目前得到的結論是: 變數引用   . 直針引用&n

C語言const禁止修改變數的值

C語言const:禁止修改變數的值 有時候我們希望定義這樣一種變數,它的值不能被改變,在整個作用域中都保持固定。例如,用一個變數來表示班級的最大人數,或者表示緩衝區大小。為了滿足這一要求,可以使用const關鍵字對變數加以限定: const int MaxNum = 100; 

孤荷凌寒自學python第十八天python變數作用範圍

孤荷凌寒自學python第十八天python函式的形參與變數的範圍   (完整學習過程螢幕記錄視訊地址在文末,手寫筆記在文末) 一、在python的函式中各種不同的形式引數在定義的先後順序上有規定: 必須形式引數,可選形式引數,*可變形式引數,**關鍵字形式引數   二、變數的

C語言給定兩個整形變數的值,將兩個值的內容進行交換

給定兩個整形變數的值,將兩個值的內容進行交換,有三種不同的方法。第一種:定義一個中間變數,使得兩個數值交換。第二種:利用兩個數值之間相互加減,使得兩個數值交換。第三種:利用異或運算,使得兩個數值交換。第一種方法程式碼如下: #include<stdio.h> int main() {

golang語言學習第一課如何定義變數,筆記及回顧

第一課主要講go語言如何定義變數 需要注意的是:go語言定義了變數一定要使用,不使用會提示出錯。這個機制比較好。 第一:完整變數定義法: var a int =3    注意:go語言跟其他語言定義變數不太一樣,型別放後面,符合人們思維的習慣。 第二種:var省略型別法: var a

六、模板語言變數,過濾器,標籤等

模板語法之變數、模版之過濾器、模版之標籤、自定義標籤和過濾器 # 相當於print了該變數 urls.py url(r'^index/', views.index), views.py from django.shortcuts import render

Java筆記成員變數,區域性變數,類變數,例項變數以及注意事項

區域性變數:方法中定義的變數。 成員變數(類似C中的全域性變數):成員變數定義在方法體和語句塊之外。成員變數就概括描述了類中的變數,不區分static。是以下變數的統稱。 類變數(靜態變數):獨立於方法之外的變數,屬於類本身。需要static修飾,事實上,類變數就是以static修飾的獨立於方法之外的成員

JavaScript嵌入的三種方式以及變數的命名規則

一、JavaScript嵌入的三種方式: 1、行間JavaScript <input type="button" name="" onclick="alert('ok!');"> 2、內嵌JavaScript: 可以寫入body,也可以寫入head

Mybatis找不到jdbc.properties中的變數的錯誤{jdbc_driverClassName}不起作用,的解決方案

Exception in thread "main" org.apache.ibatis.exceptions.PersistenceException:  ### Error querying database.  Cause: java.sql.SQLExcepti