Tải bản đầy đủ (.doc) (23 trang)

Lý Thuyết Và Bài Tập Thực Hành Golden Common Lisp - ĐH Bách Khoa TP.HCM

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (293.64 KB, 23 trang )

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







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



×