BK
TP.HCM
ĐẠI HỌC BÁCH KHOA TPHCM
KHOA CÔNG NGHỆ THÔNG TIN
MÔN HỌC : NGÔN NGỮ LẬP TRÌNH
LÝ THUYẾT VÀ BÀI TẬP THỰC HÀNH
GOLDEN COMMON LISP
TPHCM, Tháng 10 – 2004
LÝ THUYẾT VÀ BÀI TẬP THỰC HÀNH
GOLDEN COMMON LISP
Mục lục
LÝ THUYẾT VÀ BÀI TẬP THỰC HÀNH GOLDEN COMMON LISP....................................................1
LÝ THUYẾT VÀ BÀI TẬP THỰC HÀNH GOLDEN COMMON LISP....................................................2
Mục lục................................................................................................................................................. 3
Lý thuyết LISP...................................................................................................................................... 1
I. Giới thiệu............................................................................................................................................ 1
II. Cài đặt và sử dụng gcLisp............................................................................................................... 1
III. Đặc điểm của gcLisp....................................................................................................................... 3
IV. Lập trình với gcLisp........................................................................................................................ 5
V. Nâng cao......................................................................................................................................... 17
VI. Tổng kết......................................................................................................................................... 20
Lý thuyết LISP
I. Giới thiệu
-
LISP – ngôn ngữ lập trình có tên lấy từ List Processing.
-
Vào mùa hè năm 1956, Allen Newell, J.C. Shaw, và Herbert Simon đã phát triển xử lý
Lisp (Lisp processing) và tạo nên ngôn ngữ xử lý thông tin IPL (Information
Processing Language) – ngôn ngữ trừu tượng thao tác trên symbols và danh sách.
-
Khi FORTRAN được xây dựng, McCarthy thiết kế một ngôn ngữ mới – LISP (Lisp
Processor), lấy từ ý tưởng của IPL, FORTRAN và FLPL chạy trên IBM704
-
Vào thập niên 70, Guy Steele và Gerald Sussman định ra Scheme, kết hợp Algol và
Lisp
-
Vào đầu thập niên 80, có khoảng 12 hệ Lisp khác nhau. Các hệ Lisp này không tương
thích nhau. Do đó, một dự án nhằm định ra một ngôn ngữ Lisp chung nhất đã hình
thành – dự án này sẽ kết hợp những đặc tính tốt nhất của các hệ Lisp thời đó vào một
thể thống nhất
-
Phiên bản đầu tiên của Common Lisp chuẩn ra đời năm 1984 – kết hợp nhiều ý tưởng
của ngôn ngữ lập trình như thông dịch và biên dịch hàm, dọn rác (garbage collection),
gọi hàm đệ quy, theo vết và dò lỗi (tracing and debugging) và trình soạn thảo theo cú
pháp.
II. Cài đặt và sử dụng gcLisp
II.1
Cài đặt
-
Chép tập tin GC LISP.rar vào và giải nén ra thư mục \GC LISP
-
Vào thư mục \GC LISP, chạy tập tin caidat.bat để cài gcLisp vào đĩa cứng
-
Vào thư mụcC:\Gclisp, chạy tập tin Gclisp.exe để bắt đầu.
II.2
Môi trường Gclisp
Chúng ta sẽ thấy cửa sổ gclisp như sau:
Trang 1
II.3
Phím nóng
-
<Alt>-H
to get help
-
<Alt>-E
to enter the LISP Explorer
-
<Ctrl>-E
to enter the editor
-
<F1>
to exit editor into Lisp Environment
-
<F8>
to load a file into the editor
-
<F9>
to save a file
-
<F10>
to save a file as another name
II.4
Dòng lệnh
Đánh các dòng lệnh theo sau ký hiệu *
II.5
Lệnh tiền tố (Prefix command)
-
Mọi lệnh đều nằm giữa dấu ngoặc đơn ( )
-
Lisp sẽ đánh giá khi chúng ta đánh dấu ) cuối cùng
Trang 2
(function
)
-
Lệnh có dạng tiền tố:
II.6
-
Load file vào gclisp
Ví dụ: load first.lsp
* (load ‘first)
III. Đặc điểm của gcLisp
III.1 Các đặc tính của ngôn ngữ
-
Từ khi được John McCarthy (MIT) nghĩ ra năm 1958, LISP được tinh chế dần đến
version 1.5 và được sử sụng lâu dài về sau
-
Lisp là ngôn ngữ hướng chức năng (functional language hay applicative), dùng lối ký
hiệu tiền tố (prefix) và dấu ngoặc đơn:
f(x,y, z) được ký hiệu là (f x y z)
Cũng như vậy x+y ký hiệu là (+ x y)
Bt.
-
π
sin 3 x + viết trong Lisp như thế nào ?
2
( sin ( + ( * 3 x ) ( / pi 2) ) )
Lisp là ngôn ngữ thông dịch (interpreted language)
Ngôn ngữ biên dịch
Ngôn ngữ thông dịch
câu lệnh (instructions)
Biểu thức
biên dịch
chương trình thực thi
thực thi
kết quả
đánh giá
trả lời
Trang 3
Kết quả
Ví dụ:
*(+ 3 4)
7
*(+ (* 3 4)(- 5 2))
15
*4
4
III.2 Kiểu dữ liệu
Lisp là ngôn ngữ đặc trưng cho việc xử lý danh sách
Chương trình được biểu diễn bằng các danh sách và có thể thao tác trên đó như dữ liệu
(+ (* 3 4) (- 5 2))
chương trình: hàm + áp dụng vào hai đối số
dữ liệu: danh sách gồm ba thành phần
Ở top-level, khi đóng vai trò là đối số của một hàm, một danh sách luôn được xem như
là sự áp dụng một hàm
Lisp thao tác trên các loại dữ liệu:
Biểu thức
expression::= atom | list
Danh sách list::= (expression1...expressionn)
Danh sách là một chuỗi các biểu thức ngăn cách nhau bởi khoảng trắng, tất cả đặt
trong dấu ngoặc đơn
Atoms
atom::= số | chuỗi ký tự | symbols
Symbol (~ identifier): từ tạo bởi các ký tự bất kỳ, ngoại trừ (
khoảng trắng
)
‘
`
“
; và
Boolean: Lisp không có kiểu boolean. Trong Lisp, nil mang giá trị logic sai và tất cả
các biểu thức khác có giá trị đúng. Ký hiệu t dùng để chỉ trị logic đúng theo mặc
định.
Các kiểu dữ liệu được xếp theo cấp bậc như sau:
expression
list
atom
Trang
4
symbol
list
nil
...
number
interger
...
real
...
Biến trong Lisp không có kiểu dữ liệu , cùng một biến có thể có nhiều kiểu dữ liệu khác
nhau
Ví dụ:
*(setq a ‘(1 2 3))
(1 2 3)
*a
(1 2 3)
*(setq a 2)
2
*a
2
Kiểu được gán cho dữ liệu chứ không phải cho biến. Ta có thể biết kiểu dữ liệu của biến
nhờ vào các vị từ (prédicat)
(numberp E)
(stringp E)
(listp E)
(null E)
(atom E)
;;
;;
;;
;;
;;
trả
trả
trả
trả
trả
về
về
về
về
về
đúng
đúng
đúng
đúng
đúng
nếu
nếu
nếu
nếu
nếu
E
E
E
E
E
là
là
là
là
là
một số
một chuỗi ký tự
một danh sách
danh sách rỗng
atom
III.3 Vị từ (Prédicats)
Vị từ kiểu (xem phía trên)
Vị từ so sánh số: < > >= <= <>
*(< 4 5)
T
*(> 4 5)
NIL
IV. Lập trình với gcLisp
IV.1 Các hàm xử lý trên danh sách
IV.1.1 FIRST và REST – CAR và CDR
FIRST trả về phần tử đầu tiên của danh sách
REST trả về danh sách theo sau phần tử đầu tiên
Cho đến gần đây, phần lớn lập trình viên LISP vẫn dùng CAR và CDR thay cho FIRST
và REST. Ngoài chức năng tương tự, CAR và CDR có thể kết hợp với nhau.thành dạng
phức hợp CxxR, CxxxR hay CxxxxR. Mỗi x tượng trưng cho A – CAR hay D – CDR.
Trang 5
Quy ước:
*(car nil)
NIL
*(cdr nil)
NIL
Bài tập:
1. Lấy phần tử thứ ba của danh sách
(car (cdr (cdr l)))
có thể viết:
(caddr l)
CAR và CDR có thể kết hợp với nhau đến mức độ 4
Ví dụ:
(caadr l) = (car (car (cdr l)))
(cadar l) = (car (cdr (car l)))
2. Làm thế nào trích ra chuỗi example trong danh sách:
L=((this) is (an (example)) more complex)
L=((this)
(cdr l) =
(cdr (cdr
(car (cdr
(cdr (car
(car (cdr
(car (car
is (an (example)) more complex)
(is (an (example)) more complex)
l)) = ((an (example)) more complex)
(cdr l))) = (an (example))
(cdr (cdr l)))) = ((example))
(car (cdr (cdr l))))) = (example)
(cdr (car (cdr (cdr l)))))) = example
IV.1.2 CONS, APPEND, LIST
LIST trả về danh sách các đối số
*(list ‘a (+ 3 1) ‘c)
(a 4 c)
*(list ‘(a b) ‘(c d))
((a b) (c d))
*(list ‘a nil)
(a nil)
CONS thêm một phần tử vào đầu danh sách
(cons ‘a ‘(2 3))
(a 2 3)
(cons `(a b) ‘(c d))
((ab) c d)
(list `a nil)
(a)
(CAR (CONS a l)) = a
(CDR (CONS a l)) = l
Trang 6
Bt. Cho biết giá trị của các biểu thức sau:
1. (cons ‘a (cons ‘b (cons ‘c nil)))
(cons ‘a (cons ‘b (cons ‘c nil)))
= (cons ‘a (cons ‘b ‘(c)))
= (cons ‘a ‘(b c))
= (a b c)
2. (list (car ‘(car ((1) (2)))) (cdr (cdr ‘((1) (2)))))
(list (car ‘(car ((1) (2))))
(cdr (cdr ‘((1) (2)))))
= (list ‘car
(cdr (cdr ‘((1) (2)))))
= (list ‘car
(cdr ((2))))
= (list ‘car nil)
= (list ‘car nil)
= (car nil)
APPEND kết hợp các phần tử của mọi danh sách đã cho
(setq l1 ‘(a b)
l2 ‘(x y))
= (x y)
(append l1 l2)
= (a b x y)
(append l1 ‘() l2 ‘())
= (a b x y)
IV.1.3 NTHCDR, BUTLAST và LAST
NTHCDR cắt n phần tử đầu danh sách, với thông số đầu chỉ số phần tử cần cắt
*(setq l ‘(a b c d e))
(a b c d e)
*(nthcdr 2 l)
(c d e)
BUTLAST cắt n phần tử cuối danh sách, với thông số đầu là danh sách, thông số
thứ hai chỉ số phần tử cần cắt
*(setq l ‘(a b c d e))
(a b c d e)
*(butlast l 2)
(a b c)
*(butlast l 10)
NIL
LAST trả về danh sách tất cả phần tử trừ phần tử cuối cùng đã bị loại ra
*(setq l ‘(a b c d e)
l1 ‘((a b) (c d)))
((a b) (c d))
Trang 7
*(last l)
(e)
*(last l1)
((c d))
IV.1.4 LENGTH và REVERSE
LENGTH trả về chiều dài của chuỗi
*(setq l ‘(a b c d e))
(a b c d e)
*(length l)
5
REVERSE trả về chuỗi nghịch đảo
*(setq l ‘(a b c d e))
(a b c d e)
*(reverse l)
(e d c b a)
IV.1.5 ASSOC
ASSOC gắn với một danh sách – association list hay còn gọi a-list
Key
Key
(setfl sarah ‘((height .54) (weight 4.4)))
Value
Value
height và weight là khóa trong danh sách được gán cho SARAH; .54 và 4.4 là các
giá trị biểu thị bằng met và kilograms.
Có thể lấy các thành phần từ một danh sách dùng ASSOC, một khóa, và danh sách
liên kết:
(ASSOC <key> <asociation list>)
Ví dụ:
(assoc ‘weight sarah)
(WEIGHT 4.4)
Lưu ý ASSOC luôn trả về toàn bộ danh sách con tương ứng với khóa. Trong trường
hợp có nhiều danh sách con cùng khóa, danh sách đầu tiên sẽ được trả về.
IV.1.6 LISP cung cấp các thao tác trên Integer, Ratio, Floating-Point
Numbers, ...
*(/ 1.234321 1.111)
1.111
*(/ 27 9)
3
Trang 8
Tuy nhiên với trường hợp chia không chẵn, kết quả là một phân số:
*(/ 22 7)
22/7
Dùng FLOAT nếu muốn kết quả trả về là số thực có dấu phẩy động:
(float (/ 22 7))
3.14286
Dùng ROUND để làm tròn kết quả:
*(round (/ 22 7))
3
1/7
;Thương – số nguyên gần nhất
;Phần dư
*(+ round (/ 22 7)) (round (7/3)))
5
*(round (/ 5 2))
2
Một số hàm tính toán học:
*(MAX 2 4 3)
4
*(MIN 2 4 3)
2
*(expt 2 3)
8
*(expt 3 2)
9
*(expt 3.3 2.2)
13.827085
*(sqrt 9)
3
*(abs -5)
5
IV.2 Các câu lệnh điều kiện
Câu lệnh IF
(if E1 E2 E3)
Nếu E1 đúng, trả về giá trị E2 nếu không trả về giá trị E3
Ví dụ:
*(if (numberp 1) ‘(a number) ‘(not a number))
(A NUMBER)
*(if (numberp ‘a) ‘(a number) ‘(not a number))
(NOT A NUMBER)
AND
(and E1 E2 ... En) là sai nếu ít nhất một Ei sai
Trang 9
AND đánh giá các đối số từ trái sang phải và chỉ dừng khi gặp một đối số sai
Nếu mọi thông số đều đúng, and trả về thông số cuối cùng
Ví dụ:
*(setq x ‘a)
A
*x
A
*(and (numberp x) (> x 1) )
NIL
*(and (symbolp x) (list x) )
(A)
OR
(and E1 E2 ... En) là sai nếu ít nhất một Ei sai
AND đánh giá các đối số từ trái sang phải và chỉ dừng khi gặp một đối số đúng
Ví dụ:
*(setq x ‘a)
A
*x
A
*(or (numberp x) (> x 1) )
error > A is not a number
*(or (symbolp x) (list x) )
T
IV.3 Định nghĩa hàm
(defun <tên> <danh sách đối số> <thân hàm>)
<tên> Symbol
<danh sách đối số> đại diện cho các biến
<thân hàm> biểu thức
Ví dụ:
*(defun square (x) (* x x))
SQUARE
*(square 3)
9
*(defun abs(x)
(if (>= x 0) x
(* -1 x) ) )
ABS
Trang 10
IV.4 Chương trình đệ quy trong Lisp
Vòng lặp trong Lisp được thực hiện chủ yếu nhờ vào đệ quy
Ví dụ: Tính giai thừa
n!=1*2*...*n
0!=1
Trong Pascal, hàm n! được viết bằng vòng lặp:
function fac(integer:n):integer;
var i:integer
begin
fac:=1;
for i:=1 to n do
fac:=fac*i;
end
Định nghĩa đệ quy của giai thừa:
n! = 1*2*...*n-1 *n
n!=n*(n-1) nếu n≥2
(n-1)!
0!=1
Trong Lisp:
(defun fac(n)
(if (= n 0)
1
(* n fac (1- n)) ) )
Bài tập:
1. Viết hàm in ra phần tử thứ n trong danh sách
(defun nth
(if (= n
(car
(nth
(n l)
1)
l)
(1- n) (cdr l)) ) )
2. Ví dụ đệ quy chéo hay lời gọi đệ quy:
(defun pair (n)
(or (= n 0)
(impair (1- n)) ) )
(defun impair (n)
(and (<> n 0)
(pair (1- n)) ) )
IV.5 Đánh giá
‘Exp là cách viết tắt của (quote Exp)
*‘a
A
*‘‘a
Trang 11
(QUOTE A)
QUOTE không đánh giá đối số
Ngược lại với quote là hàm eval đánh giá giá trị của đối số
*(setq l ‘(a b c))
(A B C)
*(eval (list ‘car ‘l))
A
*(eval (list ‘* (1+ 3) 2))
6
Giá trị của (eval ‘Exp) là Exp
IV.6 Các dạng đặc biệt
(progn E1 ... En)
đánh giá tuần tự các biểu thức E1, ..., En từ trái sang phải và kết quả trả về là giá trị
của biểu thức En
*(progn (setq x ‘(a b c))
(append x x) )
(A B C A B C)
*(progn)
NIL
với defun và đôi khi if, kiểu progn tiềm ẩn và n-airs
(defun name(p1 … pm) E1 … En)
(defun name(p1 … pm) (progn E1 … En))
(if Test E1 E2 … En)
(if Test E1 (progn E2 … En))
điều này không đúng trong Clisp
(prog1 E1 ... En)
đánh giá tuần tự các biểu thức E1, ..., En từ trái sang phải và kết quả trả về là giá trị
của biểu thức E1
*(prog1 (setq x ‘(a b c))
(append x x) )
(A B C)
IV.7 Các trường hợp điều kiện
(cond (Test1 E1 …)
(Test2 E2 …)
(Test3 E3 …)
…
(Testn En …) )
Trang 12
(if Test1 (progn E1 …)
(if Test2 (progn E2 …)
(if Test3 (progn E3 …)
…
(if Testn (progn En …)) … )
))
Trong một mệnh đề kiểu (Test1), nếu Test1 đúng, kết quả của cond là giá trị của (Test1)
Ví dụ: Viết hàm trả về kiểu của đối số
*(type-of 1)
FIXNUM
*(type-of a)
SYMBOL
Giải:
(defun type-of (x)
(cond ((null x) ‘null)
((symbolp x) ‘symbolp)
((numberp x) ‘numberp)
((stringp x) ‘stringp)
((consp x) ‘consp)
(t ‘unknown-type) )
)
IV.8 Biến cục bộ
(let ((var1 exp1) … (varm expm))
expm+1 … expn)
Chúng ta gán cho mỗi biến giá trị của biểu thức tương ứng, sau đó ta đánh giá
(progn expm+1 … expn)
Ví dụ:
*(let ((x (fac 4))) (* x x))
= 576
Các biến cục bộ che phủ các biến toàn cục
*(setq x 5)
5
*(let ((x 1)) x)
= 1
*x
5
*(let ((x 1)) (setq x 2) x)
2
Trang 13
*x
5
Các biến cục bộ che phủ các đối số của một hàm
*(defun foo(x)
(let ((x 1)) x ) )
FOO
*(foo 4)
1
Các liên kết được thực hiện song song
*(defun bar(x)
(let ((x 1) (y (1+ x)))
y) )
BAR
*(bar 4)
5
Dạng let* thực hiện một liên kết tuần tự các đối số
*(defun bar(x)
(let ((x 1) (y (1- x)))
y) )
BAR
*(defun bar1(x)
(let* ((x 1) (y (1- x)))
y) )
BAR
*(bar 3)
2
*(bar1 3)
0
let* tương ứng với let lồng nhau
* (defun bar(x)
(let ((x 1))
(let ((y (1+ x)))
y) ) )
BAR
IV.9 Symbols
So sánh hai symbols
Có thể so sánh hai symbols nhờ hàm eq
*(eq ‘a ‘b)
NIL
*(eq ‘a ‘a)
T
Trang 14
(neq Exp) = (null (eq Exp))
Các trường của một symbol
Symbol là một đối tượng bao gồm nhiều trường:
CVAL: giá trị của symbol cũng như biến
PNAME: chuỗi ký tự tương ứng với tên của symbol (dùng cho máy in)
FVAL: hàm gắn liền với symbol, trường này không tồn tại trong LISP đơn trị
LISP đa trị (bi-valued)
LISP đơn trị (mono-valued)
*(setq + 4)
4
*(setq + 4)
4
*(+ + 3)
7
*(+ + 3)
func + undef
*(setq + *)
*(* 4 3)
= 12
var + indef
FTYPE: kiểu hàm
Ví dụ:
* (setq foo 3)
3
3
CVAL
*(defun foo(x) …)
…
FVAL
EXPR
FTYPE
“foo”
PNAME
foo
FOO
IV.10 Lập trình hướng dữ liệu
Ví dụ: Chúng ta muốn dùng cùng một hàm thực hiện việc cộng hai số và nối hai
chuỗi
Giải pháp 1
(defun add(x y)
(cond ((numberp x) (+ x y))
((listp x) (append x y) ) )
)
Giải pháp 2
(putprop ‘add ‘+ ‘numberp)
(putprop ‘add ‘append ‘listp)
(defun add(x y)
(funcall (getprop ‘add (type-of x))
x y) )
funcall cho phép gọi các hàm tính toán
Trang 15
IV.11 Lời gọi hàm tính toán
funcall: áp dụng giá trị của thông số thứ nhất (một hàm hay tên của một hàm) vào
các thông số tiếp theo
(funcall ‘F E1…En) = (F E1…En)
IV.12 Nhập/xuất cơ bản
Có thể dễ dàng viết các định nghĩa hàm trong một file (có thể edit) sau đó load file
đó lên
load name
tên hay đường dẫn đến file chứa định nghĩa hàm
(read) đọc một biểu thức và trả về kết quả
* (+ (read) 3)
* 4
= 7
* (read)
* (+ 3 4)
= (+ 3 4)
IV.13 Lưu ý
Có thể viết vòng lặp toplevel:
(defun toplevel ()
(print (eval (read)))
(toplevel) )
IV.14 Hiệu chỉnh
Đây là ngôn ngữ tương tác (interactive), do đó chúng ta có thể kiểm tra mỗi hàm ở
toplevel mà không bắt buộc phải định nghĩa các chương trình tests
Một kỹ thuật theo vết (trace) cho phép theo dõi quá trình thực hiện một hàm
*(defun fac(n)
(if (= n 0) 1 (* n (fac (1- n))) ) )
FAC
*(trace fac)
;Autoload: TRACE from “TRACE” in “C:\\GCLISP\\LISPLIB”
T
* (fac 2)
ENTERING: FAC, ARGUMENT LIST: (2)
ENTERING: FAC, ARGUMENT LIST: (1)
ENTERING: FAC, ARGUMENT LIST: (0)
EXITING: FAC, VALUE: 1
EXITING: FAC, VALUE: 1
EXITING: FAC, VALUE: 2
2
untrace cho phép trở về sự thực hiện bình thường của hàm
Trang 16
*(untrace fac)
(FAC)
*(fac 2)
2
trace cập nhật định nghĩa hàm theo vết bằng cách in ra kết quả từng giai đoạn
có thể theo dõi sự thực hiện nhiều hàm cùng lúc
V. Nâng cao
V.1
V.1.1
Doublets
Doublets
Một danh sách không rỗng được biểu diễn bằng một đối tượng có hai trường gọi là
doublet
trường trường
car
cdr
phần còn lại
phần tử đầu
Tương tự như vậy, danh sách (1 2 3) được thể hiện bởi:
nil
1
2
3
2
3
Hay đơn giản hơn:
1
V.1.2
nil
Pointed pair
Thông số thứ hai của cons có thể không phảI là một danh sách. Trong trường hợp
đó, chúng ta gọi là cặp con trỏ (pointed pair)
*(cons ‘a ‘b)
(A . B)
*(car ‘(a . b))
A
*(cdr ‘(a . b))
B
V.1.3
Ký hiệu pointed pair
Ký hiệu danh sách là viết tắt của ký hiệu pointed pair
*‘(1 . nil)
Trang 17
(1)
*‘(1 . (2 . (3 . nil)))
(1 2 3)
Nói chung, chúng ta có:
(exp.(exp1 … expN))=(exp exp1 … expN)
Chúng ta có thể kết hợp hai lối ký hiệu:
*‘(1 . (2 3))
(1 2 3)
*‘(1 . (2 . 3))
(1 2 . 3)
V.1.4
Doublets
Mỗi biểu thức trong Lisp là một doublet hay một atom
expression ::= (expression.expression) | atom
Consp là một vị từ để kiểm tra thông số của nó có là một doublet hay không
(defun listp(p)
(or (null x)
(consp x) ) )
V.2
Apply
(apply F L) áp dụng hàm F trên các phần tử của danh sách L
*(apply ‘append ‘((a b) (c d)))
(a b c d)
*(apply ‘+ ‘(1 2))
3
*(setq a ‘*)
*
*(apply a ‘(3 4))
12
(apply F L) ≡ (eval (cons F L))
Bt. Đếm số symbols, numbers hay strings trong một list
(defun count(test l)
(if (null l) 0
(if (apply test (list (car l)))
(1+ (count test (cdr l)))
(count test (cdr l)) ) )
)
V.3
Funcall và ứng dụng
(apply f (list e1 … en)) ≈ (funcall f e1 … e2)
*(setq a ‘+)
+
Trang 18
*(funcall a (+ 3 4) 5)
12
Lưu ý: Trong Lisp đơn trị (như Scheme), chúng ta có thể viết trực tiếp:
* (a (+ 3 4) 5)
= 12
vì ở đó có sự đánh giá hàm
Bt. Đếm số symbols, numbers hay strings trong một list
(defun count(test l)
(if (null l) 0
(if (funcall test (car l))
(1+ (count test (cdr l)))
(count test (cdr l)) ) )
)
Bt. Đếm số symbols, numbers hay strings trong một list
V.4
Lambda expression
Dùng hàm count để đếm số số nguyên nhỏ hơn 10 trong danh sách
*(defun inf10(x)
(and (integerp x) (< x 10)) )
INF10
*(count ‘inf10 ‘(4 12 a 11 3))
2
Nhận xét : Chúng ta định nghĩa hàm inf10 chỉ để sử dụng một lần duy nhất
Có thể sử dụng một hàm không tên. Hàm như thế trong Lisp được gọi là lambdaexpression va được viết:
(lambda header content)
*(count (lambda (x)
(and (integer x)
(< x 10) ) )
‘(4 12 a 11 3)
2
Có thể trực tiếp định nghĩa lambda-expression khi xử lý:
*(+ 10 ((lambda (x) (* x x)) 4) )
26
V.5
Hàm vô danh và biến cục bộ
Các biến cục bộ được bắt đầu với let là các tham số của một hàm vô danh (anonymous)
(let ((var1 val1) … (varN valN)) corps)
≡
( ( lambda (var1 … varN) corps) val1 … valN)
*(let ((x 1) (y 2)) (+ x y))
3
* ( ( lambda (x y) (+ x y) ) 1 2 )
Trang 19
3
VI. Tổng kết
Trang 20