LISP - Hướng dẫn nhanh

John McCarthy đã phát minh ra LISP vào năm 1958, ngay sau khi FORTRAN phát triển. Nó được thực hiện lần đầu tiên bởi Steve Russell trên máy tính IBM 704.

Nó đặc biệt thích hợp cho các chương trình Trí tuệ nhân tạo, vì nó xử lý thông tin tượng trưng một cách hiệu quả.

Common Lisp có nguồn gốc từ những năm 1980 và 1990, với nỗ lực thống nhất công việc của một số nhóm triển khai kế thừa Maclisp, như ZetaLisp và NIL (New Triển khai Lisp), v.v.

Nó phục vụ như một ngôn ngữ chung, có thể dễ dàng mở rộng để triển khai cụ thể.

Các chương trình được viết bằng Common LISP không phụ thuộc vào các đặc điểm riêng của máy, chẳng hạn như độ dài từ, v.v.

Đặc điểm của LISP chung

  • Nó không phụ thuộc vào máy móc

  • Nó sử dụng phương pháp thiết kế lặp đi lặp lại và khả năng mở rộng dễ dàng.

  • Nó cho phép cập nhật các chương trình động.

  • Nó cung cấp khả năng gỡ lỗi cấp cao.

  • Nó cung cấp lập trình hướng đối tượng nâng cao.

  • Nó cung cấp một hệ thống vĩ mô thuận tiện.

  • Nó cung cấp nhiều kiểu dữ liệu như, đối tượng, cấu trúc, danh sách, vectơ, mảng có thể điều chỉnh, bảng băm và ký hiệu.

  • Nó dựa trên biểu thức.

  • Nó cung cấp một hệ thống điều kiện hướng đối tượng.

  • Nó cung cấp một thư viện I / O hoàn chỉnh.

  • Nó cung cấp các cấu trúc điều khiển mở rộng.

Các ứng dụng được xây dựng trong LISP

Các ứng dụng thành công lớn được xây dựng trong Lisp.

  • Emacs

  • G2

  • AutoCad

  • Igor Engraver

  • Cửa hàng Yahoo

Thiết lập môi trường cục bộ

Nếu bạn vẫn sẵn sàng thiết lập môi trường của mình cho ngôn ngữ lập trình Lisp, bạn cần hai phần mềm sau có sẵn trên máy tính của mình, (a) Trình soạn thảo văn bản và (b) Máy tính thực thi Lisp.

Trình soạn thảo văn bản

Điều này sẽ được sử dụng để nhập chương trình của bạn. Ví dụ về một số trình soạn thảo bao gồm Windows Notepad, lệnh Chỉnh sửa hệ điều hành, Tóm tắt, Epsilon, EMACS và vim hoặc vi.

Tên và phiên bản của trình soạn thảo văn bản có thể khác nhau trên các hệ điều hành khác nhau. Ví dụ: Notepad sẽ được sử dụng trên Windows và vim hoặc vi có thể được sử dụng trên windows cũng như Linux hoặc UNIX.

Các tệp bạn tạo bằng trình chỉnh sửa của mình được gọi là tệp nguồn và chứa mã nguồn chương trình. Các tệp nguồn cho các chương trình Lisp thường được đặt tên với phần mở rộng ".lisp".

Trước khi bắt đầu lập trình, hãy đảm bảo rằng bạn đã có sẵn một trình soạn thảo văn bản và bạn có đủ kinh nghiệm để viết một chương trình máy tính, lưu nó vào một tệp, cuối cùng là thực thi nó.

Máy thực thi Lisp

Mã nguồn được viết trong tệp nguồn là nguồn con người có thể đọc được cho chương trình của bạn. Nó cần được "thực thi", chuyển thành ngôn ngữ máy để CPU của bạn thực sự có thể thực thi chương trình theo hướng dẫn được đưa ra.

Ngôn ngữ lập trình Lisp này sẽ được sử dụng để thực thi mã nguồn của bạn thành chương trình thực thi cuối cùng. Tôi giả sử bạn có kiến ​​thức cơ bản về một ngôn ngữ lập trình.

CLISP là trình biên dịch đa kiến ​​trúc GNU Common LISP được sử dụng để thiết lập LISP trong Windows. Phiên bản windows giả lập môi trường unix bằng MingW bên dưới windows. Trình cài đặt sẽ xử lý việc này và tự động thêm clisp vào biến Windows PATH.

Bạn có thể tải CLISP mới nhất cho Windows từ đây - https://sourceforge.net/projects/clisp/files/latest/download

Nó tạo một lối tắt trong Menu Start theo mặc định, cho trình thông dịch từng dòng.

Cách sử dụng CLISP

Trong quá trình cài đặt, clisp được tự động thêm vào biến PATH của bạn nếu bạn chọn tùy chọn (KHUYẾN CÁO) Điều này có nghĩa là bạn chỉ cần mở cửa sổ Command Prompt mới và gõ “clisp” để hiển thị trình biên dịch.

Để chạy tệp * .lisp hoặc * .lsp, chỉ cần sử dụng -

clisp hello.lisp

Biểu thức LISP được gọi là biểu thức tượng trưng hoặc biểu thức s. Biểu thức s bao gồm ba đối tượng hợp lệ, nguyên tử, danh sách và chuỗi.

Bất kỳ biểu thức s nào đều là một chương trình hợp lệ.

Các chương trình LISP chạy trên một interpreter hoặc như compiled code.

Trình thông dịch kiểm tra mã nguồn trong một vòng lặp lặp lại, còn được gọi là vòng lặp đọc-đánh giá-in (REPL). Nó đọc mã chương trình, đánh giá nó và in các giá trị mà chương trình trả về.

Một chương trình đơn giản

Chúng ta hãy viết biểu thức s để tìm tổng của ba số 7, 9 và 11. Để thực hiện việc này, chúng ta có thể nhập vào dấu nhắc thông dịch.

(+ 7 9 11)

LISP trả về kết quả -

27

Nếu bạn muốn chạy cùng một chương trình với mã đã biên dịch, thì hãy tạo tệp mã nguồn LISP có tên myprog.lisp và nhập mã sau vào đó.

(write (+ 7 9 11))

Khi bạn nhấp vào nút Execute hoặc gõ Ctrl + E, LISP sẽ thực thi nó ngay lập tức và kết quả trả về là:

27

LISP sử dụng ký hiệu tiền tố

Bạn có thể lưu ý rằng LISP sử dụng prefix notation.

Trong chương trình trên, ký hiệu + đóng vai trò là tên hàm cho quá trình tính tổng các số.

Trong ký hiệu tiền tố, các toán tử được viết trước các toán hạng của chúng. Ví dụ, biểu thức,

a * ( b + c ) / d

sẽ được viết là -

(/ (* a (+ b c) ) d)

Hãy lấy một ví dụ khác, chúng ta hãy viết mã để chuyển đổi nhiệt độ Fahrenheit là 60 ° F sang thang độ C.

Biểu thức toán học cho chuyển đổi này sẽ là:

(60 * 9 / 5) + 32

Tạo một tệp mã nguồn có tên main.lisp và nhập mã sau vào đó.

(write(+ (* (/ 9 5) 60) 32))

Khi bạn nhấp vào nút Execute hoặc gõ Ctrl + E, LISP sẽ thực thi nó ngay lập tức và kết quả trả về là−

140

Đánh giá các Chương trình LISP

Đánh giá các chương trình LISP có hai phần:

  • Dịch văn bản chương trình thành các đối tượng Lisp bằng một chương trình đọc

  • Triển khai ngữ nghĩa của ngôn ngữ theo các đối tượng này bằng một chương trình đánh giá

Quá trình đánh giá thực hiện theo các bước sau:

  • Trình đọc dịch các chuỗi ký tự thành các đối tượng LISP hoặc s-expressions.

  • Trình đánh giá xác định cú pháp của Lisp formsđược xây dựng từ biểu thức s. Mức độ đánh giá thứ hai này xác định một cú pháp xác địnhs-expressions là các hình thức LISP.

  • Bộ đánh giá hoạt động như một hàm lấy biểu mẫu LISP hợp lệ làm đối số và trả về một giá trị. Đây là lý do tại sao chúng tôi đặt biểu thức LISP trong dấu ngoặc đơn, bởi vì chúng tôi đang gửi toàn bộ biểu thức / biểu mẫu đến trình đánh giá dưới dạng đối số.

Chương trình 'Hello World'

Học một ngôn ngữ lập trình mới không thực sự thành công cho đến khi bạn học cách chào đón toàn thế giới bằng ngôn ngữ đó, đúng không!

Vì vậy, hãy tạo tệp mã nguồn mới có tên main.lisp và nhập mã sau vào đó.

(write-line "Hello World")

(write-line "I am at 'Tutorials Point'! Learning LISP")

Khi bạn nhấp vào nút Execute hoặc gõ Ctrl + E, LISP sẽ thực thi nó ngay lập tức và kết quả trả về là:

Hello World

I am at 'Tutorials Point'! Learning LISP

Khối xây dựng cơ bản trong LISP

Các chương trình LISP được tạo thành từ ba khối xây dựng cơ bản:

  • atom
  • list
  • string

An atomlà một số hoặc một chuỗi ký tự liền nhau. Nó bao gồm các số và các ký tự đặc biệt.

Sau đây là các ví dụ về một số nguyên tử hợp lệ -

hello-from-tutorials-point
name
123008907
*hello*
Block#221
abc123

A list là một chuỗi các nguyên tử và / hoặc các danh sách khác được đặt trong dấu ngoặc đơn.

Sau đây là ví dụ về một số danh sách hợp lệ -

( i am a list)
(a ( a b c) d e fgh)
(father tom ( susan bill joe))
(sun mon tue wed thur fri sat)
( )

A string là một nhóm các ký tự được đặt trong dấu ngoặc kép.

Sau đây là ví dụ về một số chuỗi hợp lệ:

" I am a string"
"a ba c d efg #$%^&!"
"Please enter the following details :"
"Hello from 'Tutorials Point'! "

Thêm nhận xét

Biểu tượng dấu chấm phẩy (;) được sử dụng để chỉ dòng chú thích.

Ví dụ,

(write-line "Hello World") ; greet the world

; tell them your whereabouts

(write-line "I am at 'Tutorials Point'! Learning LISP")

Khi bạn nhấp vào nút Execute hoặc gõ Ctrl + E, LISP sẽ thực thi nó ngay lập tức và kết quả trả về là:

Hello World

I am at 'Tutorials Point'! Learning LISP

Một số điểm đáng chú ý trước khi chuyển sang tiếp theo

Sau đây là một số điểm quan trọng cần lưu ý:

  • Các phép toán số cơ bản trong LISP là +, -, * và /

  • LISP biểu diễn một lời gọi hàm f (x) là (fx), ví dụ cos (45) được viết là cos 45

  • Biểu thức LISP không phân biệt chữ hoa chữ thường, cos 45 hoặc COS 45 giống nhau.

  • LISP cố gắng đánh giá mọi thứ, bao gồm cả các đối số của một hàm. Chỉ có ba loại phần tử là hằng số và luôn trả về giá trị riêng của chúng

    • Numbers

    • Lá thư t, đó là viết tắt của logic true.

    • Giá trị nil, đó là viết tắt của logic false, cũng như một danh sách trống.

Thông tin thêm về Biểu mẫu LISP

Trong chương trước, chúng tôi đã đề cập rằng quá trình đánh giá mã LISP thực hiện theo các bước sau.

  • Trình đọc dịch các chuỗi ký tự thành các đối tượng LISP hoặc s-expressions.

  • Trình đánh giá xác định cú pháp của Lisp formsđược xây dựng từ biểu thức s. Mức đánh giá thứ hai này xác định cú pháp xác định biểu thức s nào là dạng LISP.

Bây giờ, một biểu mẫu LISP có thể là.

  • Một nguyên tử
  • Một danh sách trống hoặc không có
  • Bất kỳ danh sách nào có ký hiệu là phần tử đầu tiên của nó

Bộ đánh giá hoạt động như một hàm lấy biểu mẫu LISP hợp lệ làm đối số và trả về một giá trị. Đây là lý do tại sao chúng tôi đặtLISP expression in parenthesis, bởi vì chúng tôi đang gửi toàn bộ biểu thức / biểu mẫu tới trình đánh giá dưới dạng đối số.

Quy ước đặt tên trong LISP

Tên hoặc ký hiệu có thể bao gồm bất kỳ số ký tự chữ và số nào khác ngoài khoảng trắng, dấu ngoặc đơn mở và đóng, dấu ngoặc kép và dấu nháy đơn, dấu gạch chéo ngược, dấu phẩy, dấu hai chấm, dấu chấm phẩy và thanh dọc. Để sử dụng các ký tự này trong tên, bạn cần sử dụng ký tự thoát (\).

Tên có thể có các chữ số nhưng không hoàn toàn được tạo bởi các chữ số, vì khi đó nó sẽ được đọc như một số. Tương tự, tên có thể có dấu chấm, nhưng không thể được tạo hoàn toàn bằng dấu chấm.

Sử dụng Dấu ngoặc kép

LISP đánh giá mọi thứ bao gồm các đối số của hàm và danh sách các thành viên.

Đôi khi, chúng ta cần xem xét các nguyên tử hoặc danh sách theo đúng nghĩa đen và không muốn chúng được đánh giá hoặc coi chúng như các lệnh gọi hàm.

Để làm điều này, chúng ta cần đặt trước nguyên tử hoặc danh sách bằng một dấu ngoặc kép.

Ví dụ sau đây chứng minh điều này.

Tạo một tệp có tên main.lisp và nhập mã sau vào tệp đó.

(write-line "single quote used, it inhibits evaluation")
(write '(* 2 3))
(write-line " ")
(write-line "single quote not used, so expression evaluated")
(write (* 2 3))

Khi bạn nhấp vào nút Execute hoặc gõ Ctrl + E, LISP sẽ thực thi nó ngay lập tức và kết quả trả về là:

single quote used, it inhibits evaluation
(* 2 3) 
single quote not used, so expression evaluated
6

Trong LISP, các biến không được nhập, nhưng các đối tượng dữ liệu thì có.

Các kiểu dữ liệu LISP có thể được phân loại như.

  • Scalar types - ví dụ, các loại số, ký tự, ký hiệu, v.v.

  • Data structures - ví dụ, danh sách, vectơ, vectơ bit và chuỗi.

Bất kỳ biến nào cũng có thể lấy bất kỳ đối tượng LISP nào làm giá trị của nó, trừ khi bạn đã khai báo nó một cách rõ ràng.

Mặc dù, không nhất thiết phải chỉ định kiểu dữ liệu cho một biến LISP, tuy nhiên, nó giúp ích trong việc mở rộng vòng lặp nhất định, trong khai báo phương thức và một số tình huống khác mà chúng ta sẽ thảo luận trong các chương sau.

Các kiểu dữ liệu được sắp xếp thành một hệ thống phân cấp. Kiểu dữ liệu là một tập hợp các đối tượng LISP và nhiều đối tượng có thể thuộc về một tập hợp đó.

Các typep vị ngữ được sử dụng để tìm xem một đối tượng có thuộc một kiểu cụ thể hay không.

Các type-of hàm trả về kiểu dữ liệu của một đối tượng nhất định.

Nhập các chỉ định trong LISP

Các chỉ định kiểu là các ký hiệu do hệ thống xác định cho các kiểu dữ liệu.

mảng fixnum gói hàng chuỗi đơn
nguyên tử Phao nổi tên đường dẫn đơn giản-vector
bignum chức năng trạng thái ngẫu nhiên phao đơn
bit bảng băm tỉ lệ tiêu chuẩn-char
bit-vector số nguyên hợp lý suối
tính cách từ khóa có thể đọc được chuỗi
[chung] danh sách sự nối tiếp [string-char]
hàm biên dịch trôi nổi dài phao ngắn Biểu tượng
phức tạp nill byte đã ký t
khuyết điểm vô giá trị mảng đơn giản byte không dấu
phao kép con số đơn giản-bit-vector vector

Ngoài các kiểu do hệ thống xác định này, bạn có thể tạo các kiểu dữ liệu của riêng mình. Khi một kiểu cấu trúc được xác định bằng cách sử dụngdefstruct chức năng, tên của kiểu cấu trúc trở thành một ký hiệu kiểu hợp lệ.

ví dụ 1

Tạo tệp mã nguồn mới có tên main.lisp và nhập mã sau vào đó.

(setq x 10)
(setq y 34.567)
(setq ch nil)
(setq n 123.78)
(setq bg 11.0e+4)
(setq r 124/2)

(print x)
(print y)
(print n)
(print ch)
(print bg)
(print r)

Khi bạn nhấp vào nút Execute hoặc gõ Ctrl + E, LISP sẽ thực thi nó ngay lập tức và kết quả trả về là:

10 
34.567 
123.78 
NIL 
110000.0 
62

Ví dụ 2

Tiếp theo, hãy kiểm tra các loại biến được sử dụng trong ví dụ trước. Tạo tệp mã nguồn mới có tên là main. lisp và nhập mã sau vào đó.

(defvar x 10)
(defvar y 34.567)
(defvar ch nil)
(defvar n 123.78)
(defvar bg 11.0e+4)
(defvar r 124/2)

(print (type-of x))
(print (type-of y))
(print (type-of n))
(print (type-of ch))
(print (type-of bg))
(print (type-of r))

Khi bạn nhấp vào nút Execute hoặc gõ Ctrl + E, LISP sẽ thực thi nó ngay lập tức và kết quả trả về là:

(INTEGER 0 281474976710655) 
SINGLE-FLOAT 
SINGLE-FLOAT 
NULL 
SINGLE-FLOAT 
(INTEGER 0 281474976710655)

Macro cho phép bạn mở rộng cú pháp của LISP chuẩn.

Về mặt kỹ thuật, macro là một hàm nhận biểu thức s làm đối số và trả về biểu mẫu LISP, sau đó được đánh giá.

Xác định Macro

Trong LISP, một macro có tên được xác định bằng cách sử dụng một macro khác có tên defmacro. Cú pháp để xác định macro là:

(defmacro macro-name (parameter-list))
"Optional documentation string."
body-form

Định nghĩa macro bao gồm tên của macro, danh sách tham số, chuỗi tài liệu tùy chọn và nội dung của biểu thức Lisp xác định công việc sẽ được thực hiện bởi macro.

Thí dụ

Hãy để chúng tôi viết một macro đơn giản có tên setTo10, macro này sẽ nhận một số và đặt giá trị của nó thành 10.

Tạo tệp mã nguồn mới có tên main.lisp và nhập mã sau vào đó.

(defmacro setTo10(num)
(setq num 10)(print num))
(setq x 25)
(print x)
(setTo10 x)

Khi bạn nhấp vào nút Execute hoặc gõ Ctrl + E, LISP sẽ thực thi nó ngay lập tức và kết quả trả về là:

25
10

Trong LISP, mỗi biến được biểu diễn bằng một symbol. Tên của biến là tên của ký hiệu và nó được lưu trữ trong ô lưu trữ của ký hiệu.

Biến toàn cục

Các biến toàn cục có giá trị vĩnh viễn trong toàn bộ hệ thống LISP và vẫn có hiệu lực cho đến khi một giá trị mới được chỉ định.

Các biến toàn cục thường được khai báo bằng cách sử dụng defvar xây dựng.

Ví dụ

(defvar x 234)
(write x)

Khi bạn bấm vào nút Execute hoặc gõ Ctrl + E, LISP sẽ thực thi nó ngay lập tức và kết quả trả về là

234

Vì không có khai báo kiểu cho các biến trong LISP, bạn chỉ định trực tiếp giá trị cho một ký hiệu với setq xây dựng.

Ví dụ

->(setq x 10)

Biểu thức trên gán giá trị 10 cho biến x. Bạn có thể tham chiếu đến biến bằng cách sử dụng chính ký hiệu làm biểu thức.

Các symbol-value chức năng cho phép bạn trích xuất giá trị được lưu trữ tại nơi lưu trữ ký hiệu.

Ví dụ

Tạo tệp mã nguồn mới có tên main.lisp và nhập mã sau vào đó.

(setq x 10)
(setq y 20)
(format t "x = ~2d y = ~2d ~%" x y)

(setq x 100)
(setq y 200)
(format t "x = ~2d y = ~2d" x y)

Khi bạn bấm vào nút Execute hoặc gõ Ctrl + E, LISP sẽ thực thi nó ngay lập tức và kết quả trả về là.

x = 10 y = 20 
x = 100 y = 200

Biến cục bộ

Các biến cục bộ được xác định trong một thủ tục nhất định. Các tham số được đặt tên là đối số trong định nghĩa hàm cũng là các biến cục bộ. Các biến cục bộ chỉ có thể truy cập trong hàm tương ứng.

Giống như các biến toàn cục, các biến cục bộ cũng có thể được tạo bằng cách sử dụng setq xây dựng.

Có hai cấu trúc khác - letprog để tạo các biến cục bộ.

Cấu trúc let có cú pháp sau.

(let ((var1  val1) (var2  val2).. (varn  valn))<s-expressions>)

Trong đó var1, var2, ..varn là tên biến và val1, val2, .. valn là giá trị ban đầu được gán cho các biến tương ứng.

Khi nào letđược thực thi, mỗi biến được gán giá trị tương ứng và cuối cùng biểu thức s được đánh giá. Giá trị của biểu thức cuối cùng được đánh giá được trả về.

Nếu bạn không bao gồm giá trị ban đầu cho một biến, nó sẽ được gán cho nil.

Thí dụ

Tạo tệp mã nguồn mới có tên main.lisp và nhập mã sau vào đó.

(let ((x 'a) (y 'b)(z 'c))
(format t "x = ~a y = ~a z = ~a" x y z))

Khi bạn bấm vào nút Execute hoặc gõ Ctrl + E, LISP sẽ thực thi nó ngay lập tức và kết quả trả về là.

x = A y = B z = C

Các prog construct cũng có danh sách các biến cục bộ làm đối số đầu tiên của nó, được theo sau bởi phần thân của prog, và bất kỳ số biểu thức s.

Các prog hàm thực thi danh sách các biểu thức s theo thứ tự và trả về nil trừ khi nó gặp một lệnh gọi hàm có tên return. Sau đó, đối số của return hàm được đánh giá và trả về.

Thí dụ

Tạo tệp mã nguồn mới có tên main.lisp và nhập mã sau vào đó.

(prog ((x '(a b c))(y '(1 2 3))(z '(p q 10)))
(format t "x = ~a y = ~a z = ~a" x y z))

Khi bạn bấm vào nút Execute hoặc gõ Ctrl + E, LISP sẽ thực thi nó ngay lập tức và kết quả trả về là.

x = (A B C) y = (1 2 3) z = (P Q 10)

Trong LISP, hằng là các biến không bao giờ thay đổi giá trị của chúng trong quá trình thực thi chương trình. Hằng số được khai báo bằng cách sử dụngdefconstant xây dựng.

Thí dụ

Ví dụ sau đây cho thấy việc khai báo một hằng số toàn cục PI và sau đó sử dụng giá trị này bên trong một hàm có tên là area-circle tính diện tích của một hình tròn.

The defun construct is used for defining a function, we will look into it in the Functions chapter.

Create a new source code file named main.lisp and type the following code in it.

(defconstant PI 3.141592)
(defun area-circle(rad)
   (terpri)
   (format t "Radius: ~5f" rad)
   (format t "~%Area: ~10f" (* PI rad rad)))
(area-circle 10)

When you click the Execute button, or type Ctrl+E, LISP executes it immediately and the result returned is.

Radius:  10.0
Area:   314.1592

An operator is a symbol that tells the compiler to perform specific mathematical or logical manipulations. LISP allows numerous operations on data, supported by various functions, macros and other constructs.

The operations allowed on data could be categorized as −

  • Arithmetic Operations
  • Comparison Operations
  • Logical Operations
  • Bitwise Operations

Arithmetic Operations

The following table shows all the arithmetic operators supported by LISP. Assume variable A holds 10 and variable B holds 20 then −

Show Examples

Operator Description Example
+ Adds two operands (+A B) will give 30
- Subtracts second operand from the first (- A B) will give -10
* Multiplies both operands (* A B) will give 200
/ Divides numerator by de-numerator (/ B A) will give 2
mod,rem Modulus Operator and remainder of after an integer division (mod B A )will give 0
incf Increments operator increases integer value by the second argument specified (incf A 3) will give 13
decf Decrements operator decreases integer value by the second argument specified (decf A 4) will give 9

Comparison Operations

Following table shows all the relational operators supported by LISP that compares between numbers. However unlike relational operators in other languages, LISP comparison operators may take more than two operands and they work on numbers only.

Assume variable A holds 10 and variable B holds 20, then −

Show Examples

Operator Description Example
= Checks if the values of the operands are all equal or not, if yes then condition becomes true. (= A B) is not true.
/= Checks if the values of the operands are all different or not, if values are not equal then condition becomes true. (/= A B) is true.
> Checks if the values of the operands are monotonically decreasing. (> A B) is not true.
< Checks if the values of the operands are monotonically increasing. (< A B) is true.
>= Checks if the value of any left operand is greater than or equal to the value of next right operand, if yes then condition becomes true. (>= A B) is not true.
<= Checks if the value of any left operand is less than or equal to the value of its right operand, if yes then condition becomes true. (<= A B) is true.
max It compares two or more arguments and returns the maximum value. (max A B) returns 20
min It compares two or more arguments and returns the minimum value. (min A B) returns 10

Logical Operations on Boolean Values

Common LISP provides three logical operators: and, or, and not that operates on Boolean values. Assume A has value nil and B has value 5, then −

Show Examples

Operator Description Example
and It takes any number of arguments. The arguments are evaluated left to right. If all arguments evaluate to non-nil, then the value of the last argument is returned. Otherwise nil is returned. (and A B) will return NIL.
or It takes any number of arguments. The arguments are evaluated left to right until one evaluates to non-nil, in such case the argument value is returned, otherwise it returns nil. (or A B) will return 5.
not It takes one argument and returns t if the argument evaluates to nil. (not A) will return T.

Bitwise Operations on Numbers

Bitwise operators work on bits and perform bit-by-bit operation. The truth tables for bitwise and, or, and xor operations are as follows −

Show Examples

p q p and q p or q p xor q
0 0 0 0 0
0 1 0 1 1
1 1 1 1 0
1 0 0 1 1
Assume if A = 60; and B = 13; now in binary format they will be as follows:
A = 0011 1100
B = 0000 1101
-----------------
A and B = 0000 1100
A or B = 0011 1101
A xor B = 0011 0001
not A  = 1100 0011

The Bitwise operators supported by LISP are listed in the following table. Assume variable A holds 60 and variable B holds 13, then −

Operator Description Example
logand This returns the bit-wise logical AND of its arguments. If no argument is given, then the result is -1, which is an identity for this operation. (logand a b)) will give 12
logior This returns the bit-wise logical INCLUSIVE OR of its arguments. If no argument is given, then the result is zero, which is an identity for this operation. (logior a b) will give 61
logxor This returns the bit-wise logical EXCLUSIVE OR of its arguments. If no argument is given, then the result is zero, which is an identity for this operation. (logxor a b) will give 49
lognor This returns the bit-wise NOT of its arguments. If no argument is given, then the result is -1, which is an identity for this operation. (lognor a b) will give -62,
logeqv This returns the bit-wise logical EQUIVALENCE (also known as exclusive nor) of its arguments. If no argument is given, then the result is -1, which is an identity for this operation. (logeqv a b) will give -50

Decision making structures require that the programmer specify one or more conditions to be evaluated or tested by the program, along with a statement or statements to be executed if the condition is determined to be true, and optionally, other statements to be executed if the condition is determined to be false.

Following is the general form of a typical decision making structure found in most of the programming languages −

LISP provides following types of decision making constructs. Click the following links to check their detail.

Sr.No. Construct & Description
1 cond

This construct is used for used for checking multiple test-action clauses. It can be compared to the nested if statements in other programming languages.

2 if

The if construct has various forms. In simplest form it is followed by a test clause, a test action and some other consequent action(s). If the test clause evaluates to true, then the test action is executed otherwise, the consequent clause is evaluated.

3 when

In simplest form it is followed by a test clause, and a test action. If the test clause evaluates to true, then the test action is executed otherwise, the consequent clause is evaluated.

4 case

This construct implements multiple test-action clauses like the cond construct. However, it evaluates a key form and allows multiple action clauses based on the evaluation of that key form.

There may be a situation, when you need to execute a block of code numbers of times. A loop statement allows us to execute a statement or group of statements multiple times and following is the general form of a loop statement in most of the programming languages.

LISP provides the following types of constructs to handle looping requirements. Click the following links to check their detail.

Sr.No. Construct & Description
1 loop

The loop construct is the simplest form of iteration provided by LISP. In its simplest form, it allows you to execute some statement(s) repeatedly until it finds a return statement.

2 loop for

The loop for construct allows you to implement a for-loop like iteration as most common in other languages.

3 do

The do construct is also used for performing iteration using LISP. It provides a structured form of iteration.

4 dotimes

The dotimes construct allows looping for some fixed number of iterations.

5 dolist

The dolist construct allows iteration through each element of a list.

Gracefully Exiting From a Block

The block and return-from allows you to exit gracefully from any nested blocks in case of any error.

The block function allows you to create a named block with a body composed of zero or more statements. Syntax is −

(block block-name(
...
...
))

The return-from function takes a block name and an optional (the default is nil) return value.

The following example demonstrates this −

Example

Create a new source code file named main.lisp and type the following code in it −

(defun demo-function (flag)
   (print 'entering-outer-block)
   
   (block outer-block
      (print 'entering-inner-block)
      (print (block inner-block

         (if flag
            (return-from outer-block 3)
            (return-from inner-block 5)
         )

         (print 'This-wil--not-be-printed))
      )

      (print 'left-inner-block)
      (print 'leaving-outer-block)
   t)
)
(demo-function t)
(terpri)
(demo-function nil)

When you click the Execute button, or type Ctrl+E, LISP executes it immediately and the result returned is −

ENTERING-OUTER-BLOCK 
ENTERING-INNER-BLOCK 

ENTERING-OUTER-BLOCK 
ENTERING-INNER-BLOCK 
5 
LEFT-INNER-BLOCK 
LEAVING-OUTER-BLOCK

A function is a group of statements that together perform a task.

You can divide up your code into separate functions. How you divide up your code among different functions is up to you, but logically the division usually is so each function performs a specific task.

Defining Functions in LISP

The macro named defun is used for defining functions. The defun macro needs three arguments −

  • Name of the function
  • Parameters of the function
  • Body of the function

Syntax for defun is −

(defun name (parameter-list) "Optional documentation string." body)

Let us illustrate the concept with simple examples.

Example 1

Let's write a function named averagenum that will print the average of four numbers. We will send these numbers as parameters.

Create a new source code file named main.lisp and type the following code in it.

(defun averagenum (n1 n2 n3 n4)
   (/ ( + n1 n2 n3 n4) 4)
)
(write(averagenum 10 20 30 40))

When you execute the code, it returns the following result −

25

Example 2

Let's define and call a function that would calculate the area of a circle when the radius of the circle is given as an argument.

Create a new source code file named main.lisp and type the following code in it.

(defun area-circle(rad)
   "Calculates area of a circle with given radius"
   (terpri)
   (format t "Radius: ~5f" rad)
   (format t "~%Area: ~10f" (* 3.141592 rad rad))
)
(area-circle 10)

When you execute the code, it returns the following result −

Radius:  10.0
Area:   314.1592

Please note that −

  • You can provide an empty list as parameters, which means the function takes no arguments, the list is empty, written as ().

  • LISP also allows optional, multiple, and keyword arguments.

  • The documentation string describes the purpose of the function. It is associated with the name of the function and can be obtained using the documentation function.

  • The body of the function may consist of any number of Lisp expressions.

  • The value of the last expression in the body is returned as the value of the function.

  • You can also return a value from the function using the return-from special operator.

Let us discuss the above concepts in brief. Click following links to find details −

  • Optional Parameters

  • Rest Parameters

  • Keyword Parameters

  • Returning Values from a Function

  • Lambda Functions

  • Mapping Functions

Predicates are functions that test their arguments for some specific conditions and returns nil if the condition is false, or some non-nil value is the condition is true.

The following table shows some of the most commonly used predicates −

Sr.No. Predicate & Description
1

atom

It takes one argument and returns t if the argument is an atom or nil if otherwise.

2

equal

It takes two arguments and returns t if they are structurally equal or nil otherwise.

3

eq

It takes two arguments and returns t if they are same identical objects, sharing the same memory location or nil otherwise.

4

eql

It takes two arguments and returns t if the arguments are eq, or if they are numbers of the same type with the same value, or if they are character objects that represent the same character, or nil otherwise.

5

evenp

It takes one numeric argument and returns t if the argument is even number or nil if otherwise.

6

oddp

It takes one numeric argument and returns t if the argument is odd number or nil if otherwise.

7

zerop

It takes one numeric argument and returns t if the argument is zero or nil if otherwise.

8

null

It takes one argument and returns t if the argument evaluates to nil, otherwise it returns nil.

9

listp

It takes one argument and returns t if the argument evaluates to a list otherwise it returns nil.

10

greaterp

It takes one or more argument and returns t if either there is a single argument or the arguments are successively larger from left to right, or nil if otherwise.

11

lessp

It takes one or more argument and returns t if either there is a single argument or the arguments are successively smaller from left to right, or nil if otherwise.

12

numberp

It takes one argument and returns t if the argument is a number or nil if otherwise.

13

symbolp

It takes one argument and returns t if the argument is a symbol otherwise it returns nil.

14

integerp

It takes one argument and returns t if the argument is an integer otherwise it returns nil.

15

rationalp

It takes one argument and returns t if the argument is rational number, either a ratio or a number, otherwise it returns nil.

16

floatp

It takes one argument and returns t if the argument is a floating point number otherwise it returns nil.

17

realp

It takes one argument and returns t if the argument is a real number otherwise it returns nil.

18

complexp

It takes one argument and returns t if the argument is a complex number otherwise it returns nil.

19

characterp

It takes one argument and returns t if the argument is a character otherwise it returns nil.

20

stringp

It takes one argument and returns t if the argument is a string object otherwise it returns nil.

21

arrayp

It takes one argument and returns t if the argument is an array object otherwise it returns nil.

22

packagep

It takes one argument and returns t if the argument is a package otherwise it returns nil.

Example 1

Create a new source code file named main.lisp and type the following code in it.

(write (atom 'abcd))
(terpri)
(write (equal 'a 'b))
(terpri)
(write (evenp 10))
(terpri)
(write (evenp 7 ))
(terpri)
(write (oddp 7 ))
(terpri)
(write (zerop 0.0000000001))
(terpri)
(write (eq 3 3.0 ))
(terpri)
(write (equal 3 3.0 ))
(terpri)
(write (null nil ))

When you execute the code, it returns the following result −

T
NIL
T
NIL
T
NIL
NIL
NIL
T

Example 2

Create a new source code file named main.lisp and type the following code in it.

(defun factorial (num)
   (cond ((zerop num) 1)
      (t ( * num (factorial (- num 1))))
   )
)
(setq n 6)
(format t "~% Factorial ~d is: ~d" n (factorial n))

When you execute the code, it returns the following result −

Factorial 6 is: 720

Common Lisp defines several kinds of numbers. The number data type includes various kinds of numbers supported by LISP.

The number types supported by LISP are −

  • Integers
  • Ratios
  • Floating-point numbers
  • Complex numbers

The following diagram shows the number hierarchy and various numeric data types available in LISP −

Various Numeric Types in LISP

The following table describes various number type data available in LISP −

Sr.No. Data type & Description
1

fixnum

This data type represents integers which are not too large and mostly in the range -215 to 215-1 (it is machine-dependent)

2

bignum

These are very large numbers with size limited by the amount of memory allocated for LISP, they are not fixnum numbers.

3

ratio

Represents the ratio of two numbers in the numerator/denominator form. The / function always produce the result in ratios, when its arguments are integers.

4

float

It represents non-integer numbers. There are four float data types with increasing precision.

5

complex

It represents complex numbers, which are denoted by #c. The real and imaginary parts could be both either rational or floating point numbers.

Example

Create a new source code file named main.lisp and type the following code in it.

(write (/ 1 2))
(terpri)
(write ( + (/ 1 2) (/ 3 4)))
(terpri)
(write ( + #c( 1 2) #c( 3 -4)))

When you execute the code, it returns the following result −

1/2
5/4
#C(4 -2)

Number Functions

The following table describes some commonly used numeric functions −

Sr.No. Function & Description
1

+, -, *, /

Respective arithmetic operations

2

sin, cos, tan, acos, asin, atan

Respective trigonometric functions.

3

sinh, cosh, tanh, acosh, asinh, atanh

Respective hyperbolic functions.

4

exp

Exponentiation function. Calculates ex

5

expt

Exponentiation function, takes base and power both.

6

sqrt

It calculates the square root of a number.

7

log

Logarithmic function. It one parameter is given, then it calculates its natural logarithm, otherwise the second parameter is used as base.

8

conjugate

It calculates the complex conjugate of a number. In case of a real number, it returns the number itself.

9

abs

It returns the absolute value (or magnitude) of a number.

10

gcd

It calculates the greatest common divisor of the given numbers.

11

lcm

It calculates the least common multiple of the given numbers.

12

isqrt

It gives the greatest integer less than or equal to the exact square root of a given natural number.

13

floor, ceiling, truncate, round

All these functions take two arguments as a number and returns the quotient; floor returns the largest integer that is not greater than ratio, ceiling chooses the smaller integer that is larger than ratio, truncate chooses the integer of the same sign as ratio with the largest absolute value that is less than absolute value of ratio, and round chooses an integer that is closest to ratio.

14

ffloor, fceiling, ftruncate, fround

Does the same as above, but returns the quotient as a floating point number.

15

mod, rem

Returns the remainder in a division operation.

16

float

Converts a real number to a floating point number.

17

rational, rationalize

Converts a real number to rational number.

18

numerator, denominator

Returns the respective parts of a rational number.

19

realpart, imagpart

Returns the real and imaginary part of a complex number.

Example

Create a new source code file named main.lisp and type the following code in it.

(write (/ 45 78))
(terpri)
(write (floor 45 78))
(terpri)
(write (/ 3456 75))
(terpri)
(write (floor 3456 75))
(terpri)
(write (ceiling 3456 75))
(terpri)
(write (truncate 3456 75))
(terpri)
(write (round 3456 75))
(terpri)
(write (ffloor 3456 75))
(terpri)
(write (fceiling 3456 75))
(terpri)
(write (ftruncate 3456 75))
(terpri)
(write (fround 3456 75))
(terpri)
(write (mod 3456 75))
(terpri)
(setq c (complex 6 7))
(write c)
(terpri)
(write (complex 5 -9))
(terpri)
(write (realpart c))
(terpri)
(write (imagpart c))

When you execute the code, it returns the following result −

15/26
0
1152/25
46
47
46
46
46.0
47.0
46.0
46.0
6
#C(6 7)
#C(5 -9)
6
7

In LISP, characters are represented as data objects of type character.

You can denote a character object preceding #\ before the character itself. For example, #\a means the character a.

Space and other special characters can be denoted by preceding #\ before the name of the character. For example, #\SPACE represents the space character.

The following example demonstrates this −

Example

Create a new source code file named main.lisp and type the following code in it.

(write 'a)
(terpri)
(write #\a)
(terpri)
(write-char #\a)
(terpri)
(write-char 'a)

When you execute the code, it returns the following result −

A
#\a
a
*** - WRITE-CHAR: argument A is not a character

Special Characters

Common LISP allows using the following special characters in your code. They are called the semi-standard characters.

  • #\Backspace
  • #\Tab
  • #\Linefeed
  • #\Page
  • #\Return
  • #\Rubout

Character Comparison Functions

Numeric comparison functions and operators, like, < and > do not work on characters. Common LISP provides other two sets of functions for comparing characters in your code.

One set is case-sensitive and the other case-insensitive.

The following table provides the functions −

Case Sensitive Functions Case-insensitive Functions Description
char= char-equal Checks if the values of the operands are all equal or not, if yes then condition becomes true.
char/= char-not-equal Checks if the values of the operands are all different or not, if values are not equal then condition becomes true.
char< char-lessp Checks if the values of the operands are monotonically decreasing.
char> char-greaterp Checks if the values of the operands are monotonically increasing.
char<= char-not-greaterp Checks if the value of any left operand is greater than or equal to the value of next right operand, if yes then condition becomes true.
char>= char-not-lessp Checks if the value of any left operand is less than or equal to the value of its right operand, if yes then condition becomes true.

Example

Create a new source code file named main.lisp and type the following code in it.

; case-sensitive comparison
(write (char= #\a #\b))
(terpri)
(write (char= #\a #\a))
(terpri)
(write (char= #\a #\A))
(terpri)
   
;case-insensitive comparision
(write (char-equal #\a #\A))
(terpri)
(write (char-equal #\a #\b))
(terpri)
(write (char-lessp #\a #\b #\c))
(terpri)
(write (char-greaterp #\a #\b #\c))

When you execute the code, it returns the following result −

NIL
T
NIL
T
NIL
T
NIL

LISP allows you to define single or multiple-dimension arrays using the make-array function. An array can store any LISP object as its elements.

All arrays consist of contiguous memory locations. The lowest address corresponds to the first element and the highest address to the last element.

The number of dimensions of an array is called its rank.

In LISP, an array element is specified by a sequence of non-negative integer indices. The length of the sequence must equal the rank of the array. Indexing starts from zero.

For example, to create an array with 10- cells, named my-array, we can write −

(setf my-array (make-array '(10)))

The aref function allows accessing the contents of the cells. It takes two arguments, the name of the array and the index value.

For example, to access the content of the tenth cell, we write −

(aref my-array 9)

Example 1

Create a new source code file named main.lisp and type the following code in it.

(write (setf my-array (make-array '(10))))
(terpri)
(setf (aref my-array 0) 25)
(setf (aref my-array 1) 23)
(setf (aref my-array 2) 45)
(setf (aref my-array 3) 10)
(setf (aref my-array 4) 20)
(setf (aref my-array 5) 17)
(setf (aref my-array 6) 25)
(setf (aref my-array 7) 19)
(setf (aref my-array 8) 67)
(setf (aref my-array 9) 30)
(write my-array)

When you execute the code, it returns the following result −

#(NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL)
#(25 23 45 10 20 17 25 19 67 30)

Ví dụ 2

Hãy để chúng tôi tạo một mảng 3 x 3.

Tạo một tệp mã nguồn mới có tên main.lisp và nhập mã sau vào đó.

(setf x (make-array '(3 3) 
   :initial-contents '((0 1 2 ) (3 4 5) (6 7 8)))
)
(write x)

Khi bạn thực thi mã, nó trả về kết quả sau:

#2A((0 1 2) (3 4 5) (6 7 8))

Ví dụ 3

Tạo một tệp mã nguồn mới có tên main.lisp và nhập mã sau vào đó.

(setq a (make-array '(4 3)))
(dotimes (i 4)
   (dotimes (j 3)
      (setf (aref a i j) (list i 'x j '= (* i j)))
   )
)
(dotimes (i 4)
   (dotimes (j 3)
      (print (aref a i j))
   )
)

Khi bạn thực thi mã, nó trả về kết quả sau:

(0 X 0 = 0) 
(0 X 1 = 0) 
(0 X 2 = 0) 
(1 X 0 = 0) 
(1 X 1 = 1) 
(1 X 2 = 2) 
(2 X 0 = 0) 
(2 X 1 = 2) 
(2 X 2 = 4) 
(3 X 0 = 0) 
(3 X 1 = 3) 
(3 X 2 = 6)

Cú pháp hoàn chỉnh cho hàm make-array

Hàm make-array nhận nhiều đối số khác. Chúng ta hãy xem cú pháp đầy đủ của hàm này:

make-array dimensions :element-type :initial-element :initial-contents :adjustable :fill-pointer  :displaced-to :displaced-index-offset

Ngoài đối số thứ nguyên , tất cả các đối số khác đều là từ khóa. Bảng sau đây cung cấp mô tả ngắn gọn về các đối số.

Sr.No. Lập luận & Mô tả
1

dimensions

Nó cung cấp các kích thước của mảng. Nó là một số cho mảng một chiều và một danh sách cho mảng nhiều chiều.

2

:element-type

Nó là định nghĩa kiểu, giá trị mặc định là T, tức là bất kỳ kiểu nào

3

:initial-element

Giá trị phần tử ban đầu. Nó sẽ tạo một mảng với tất cả các phần tử được khởi tạo thành một giá trị cụ thể.

4

:initial-content

Nội dung ban đầu như đối tượng.

5

:adjustable

Nó giúp tạo ra một vectơ có thể thay đổi kích thước (hoặc điều chỉnh) có bộ nhớ cơ bản có thể được thay đổi kích thước. Đối số là giá trị Boolean cho biết mảng có thể điều chỉnh được hay không, giá trị mặc định là NIL.

6

:fill-pointer

Nó theo dõi số lượng phần tử thực sự được lưu trữ trong một vector có thể thay đổi kích thước.

7

:displaced-to

Nó giúp tạo một mảng đã thay thế hoặc mảng được chia sẻ chia sẻ nội dung của nó với mảng được chỉ định. Cả hai mảng phải có cùng loại phần tử. Tùy chọn: displaced-to có thể không được sử dụng với tùy chọn: initial-element hoặc: initial-content. Đối số này mặc định là nil.

số 8

:displaced-index-offset

Nó cung cấp bù đắp chỉ mục của mảng được chia sẻ đã tạo.

Ví dụ 4

Tạo một tệp mã nguồn mới có tên main.lisp và nhập mã sau vào đó.

(setq myarray (make-array '(3 2 3) 
   :initial-contents 
   '(((a b c) (1 2 3)) 
      ((d e f) (4 5 6)) 
      ((g h i) (7 8 9)) 
   ))
) 
(setq array2 (make-array 4 :displaced-to myarray :displaced-index-offset 2)) 
(write myarray)
(terpri)
(write array2)

Khi bạn thực thi mã, nó trả về kết quả sau:

#3A(((A B C) (1 2 3)) ((D E F) (4 5 6)) ((G H I) (7 8 9)))
#(C 1 2 3)

Nếu mảng đã dời là hai chiều -

(setq myarray (make-array '(3 2 3) 
   :initial-contents 
   '(((a b c) (1 2 3)) 
      ((d e f) (4 5 6)) 
      ((g h i) (7 8 9)) 
   ))
) 
(setq array2 (make-array '(3 2) :displaced-to myarray :displaced-index-offset 2)) 
(write myarray)
(terpri)
(write array2)

Khi bạn thực thi mã, nó trả về kết quả sau:

#3A(((A B C) (1 2 3)) ((D E F) (4 5 6)) ((G H I) (7 8 9)))
#2A((C 1) (2 3) (D E))

Hãy thay đổi bù đắp chỉ mục đã dời thành 5 -

(setq myarray (make-array '(3 2 3) 
   :initial-contents 
   '(((a b c) (1 2 3)) 
      ((d e f) (4 5 6)) 
      ((g h i) (7 8 9)) 
   ))
) 
(setq array2 (make-array '(3 2) :displaced-to myarray :displaced-index-offset 5)) 
(write myarray)
(terpri)
(write array2)

Khi bạn thực thi mã, nó trả về kết quả sau:

#3A(((A B C) (1 2 3)) ((D E F) (4 5 6)) ((G H I) (7 8 9)))
#2A((3 D) (E F) (4 5))

Ví dụ 5

Tạo một tệp mã nguồn mới có tên main.lisp và nhập mã sau vào đó.

;a one dimensional array with 5 elements, 
;initail value 5
(write (make-array 5 :initial-element 5))
(terpri)

;two dimensional array, with initial element a
(write (make-array '(2 3) :initial-element 'a))
(terpri)

;an array of capacity 14, but fill pointer 5, is 5
(write(length (make-array 14 :fill-pointer 5)))
(terpri)

;however its length is 14
(write (array-dimensions (make-array 14 :fill-pointer 5)))
(terpri)

; a bit array with all initial elements set to 1
(write(make-array 10 :element-type 'bit :initial-element 1))
(terpri)

; a character array with all initial elements set to a
; is a string actually
(write(make-array 10 :element-type 'character :initial-element #\a)) 
(terpri)

; a two dimensional array with initial values a
(setq myarray (make-array '(2 2) :initial-element 'a :adjustable t))
(write myarray)
(terpri)

;readjusting the array
(adjust-array myarray '(1 3) :initial-element 'b) 
(write myarray)

Khi bạn thực thi mã, nó trả về kết quả sau:

#(5 5 5 5 5)
#2A((A A A) (A A A))
5
(14)
#*1111111111
"aaaaaaaaaa"
#2A((A A) (A A))
#2A((A A B))

Các chuỗi trong Common Lisp là các vectơ, tức là mảng một chiều các ký tự.

Các ký tự chuỗi được đặt trong dấu ngoặc kép. Bất kỳ ký tự nào được hỗ trợ bởi bộ ký tự đều có thể được đặt trong dấu ngoặc kép để tạo thành một chuỗi, ngoại trừ ký tự dấu ngoặc kép (") và ký tự thoát (\). Tuy nhiên, bạn có thể bao gồm các ký tự này bằng cách thoát chúng bằng dấu gạch chéo ngược (\).

Thí dụ

Tạo một tệp mã nguồn mới có tên main.lisp và nhập mã sau vào đó.

(write-line "Hello World")
(write-line "Welcome to Tutorials Point")

;escaping the double quote character
(write-line "Welcome to \"Tutorials Point\"")

Khi bạn thực thi mã, nó trả về kết quả sau:

Hello World
Welcome to Tutorials Point
Welcome to "Tutorials Point"

Các chức năng so sánh chuỗi

Các hàm và toán tử so sánh số, như <và> không hoạt động trên chuỗi. Common LISP cung cấp hai bộ hàm khác để so sánh các chuỗi trong mã của bạn. Một bộ phân biệt chữ hoa chữ thường và một tập hợp không phân biệt chữ hoa chữ thường.

Bảng sau cung cấp các chức năng:

Các chức năng phân biệt chữ hoa chữ thường Các chức năng phân biệt chữ hoa chữ thường Sự miêu tả
string = chuỗi bằng Kiểm tra xem giá trị của các toán hạng có bằng nhau hay không, nếu có thì điều kiện trở thành true.
string / = chuỗi-không-bằng Kiểm tra xem giá trị của các toán hạng có khác nhau hay không, nếu các giá trị không bằng nhau thì điều kiện trở thành true.
chuỗi < string-lessp Kiểm tra xem giá trị của toán hạng có giảm đơn điệu hay không.
chuỗi> string-greatp Kiểm tra xem giá trị của các toán hạng có tăng đơn điệu hay không.
chuỗi <= string-not-greatp Kiểm tra xem giá trị của bất kỳ toán hạng bên trái nào lớn hơn hoặc bằng giá trị của toán hạng bên phải tiếp theo hay không, nếu có thì điều kiện trở thành true.
chuỗi> = string-not-lessp Kiểm tra xem giá trị của bất kỳ toán hạng bên trái nào nhỏ hơn hoặc bằng giá trị của toán hạng bên phải của nó hay không, nếu có thì điều kiện trở thành true.

Thí dụ

Tạo một tệp mã nguồn mới có tên main.lisp và nhập mã sau vào đó.

; case-sensitive comparison
(write (string= "this is test" "This is test"))
(terpri)
(write (string> "this is test" "This is test"))
(terpri)
(write (string< "this is test" "This is test"))
(terpri)

;case-insensitive comparision
(write (string-equal "this is test" "This is test"))
(terpri)
(write (string-greaterp "this is test" "This is test"))
(terpri)
(write (string-lessp "this is test" "This is test"))
(terpri)

;checking non-equal
(write (string/= "this is test" "this is Test"))
(terpri)
(write (string-not-equal "this is test" "This is test"))
(terpri)
(write (string/= "lisp" "lisping"))
(terpri)
(write (string/= "decent" "decency"))

Khi bạn thực thi mã, nó trả về kết quả sau:

NIL
0
NIL
T
NIL
NIL
8
NIL
4
5

Chức năng kiểm soát trường hợp

Bảng sau đây mô tả các chức năng điều khiển trường hợp:

Sr.No. Mô tả chức năng
1

string-upcase

Chuyển chuỗi thành chữ hoa

2

string-downcase

Chuyển đổi chuỗi thành chữ thường

3

string-capitalize

Viết hoa từng từ trong chuỗi

Thí dụ

Tạo một tệp mã nguồn mới có tên main.lisp và nhập mã sau vào đó.

(write-line (string-upcase "a big hello from tutorials point"))
(write-line (string-capitalize "a big hello from tutorials point"))

Khi bạn thực thi mã, nó trả về kết quả sau:

A BIG HELLO FROM TUTORIALS POINT
A Big Hello From Tutorials Point

Cắt tỉa chuỗi

Bảng sau đây mô tả các hàm cắt xén chuỗi:

Sr.No. Mô tả chức năng
1

string-trim

Nó nhận một chuỗi ký tự làm đối số đầu tiên và một chuỗi làm đối số thứ hai và trả về một chuỗi con trong đó tất cả các ký tự có trong đối số đầu tiên bị xóa khỏi chuỗi đối số.

2

String-left-trim

Nó nhận một chuỗi ký tự làm đối số đầu tiên và một chuỗi làm đối số thứ hai và trả về một chuỗi con trong đó tất cả các ký tự có trong đối số đầu tiên bị xóa khỏi đầu chuỗi đối số.

3

String-right-trim

Nó nhận (các) ký tự chuỗi làm đối số đầu tiên và một chuỗi làm đối số thứ hai và trả về một chuỗi con trong đó tất cả các ký tự có trong đối số đầu tiên bị xóa khỏi phần cuối của chuỗi đối số.

Thí dụ

Tạo một tệp mã nguồn mới có tên main.lisp và nhập mã sau vào đó.

(write-line (string-trim " " "   a big hello from tutorials point   "))
(write-line (string-left-trim " " "   a big hello from tutorials point   "))
(write-line (string-right-trim " " "   a big hello from tutorials point   "))
(write-line (string-trim " a" "   a big hello from tutorials point   "))

Khi bạn thực thi mã, nó trả về kết quả sau:

a big hello from tutorials point
a big hello from tutorials point   
   a big hello from tutorials point
big hello from tutorials point

Các hàm chuỗi khác

Chuỗi trong LISP là mảng và do đó cũng là chuỗi. Chúng tôi sẽ đề cập đến các loại dữ liệu này trong các hướng dẫn sắp tới. Tất cả các hàm có thể áp dụng cho mảng và chuỗi cũng áp dụng cho chuỗi. Tuy nhiên, chúng tôi sẽ trình bày một số hàm thường được sử dụng bằng các ví dụ khác nhau.

Tính chiều dài

Các length hàm tính toán độ dài của một chuỗi.

Giải nén chuỗi con

Các subseq hàm trả về một chuỗi con (vì một chuỗi cũng là một chuỗi) bắt đầu từ một chỉ mục cụ thể và tiếp tục đến một chỉ mục kết thúc cụ thể hoặc cuối chuỗi.

Truy cập một ký tự trong một chuỗi

Các char hàm cho phép truy cập các ký tự riêng lẻ của một chuỗi.

Example

Tạo một tệp mã nguồn mới có tên main.lisp và nhập mã sau vào đó.

(write (length "Hello World"))
(terpri)
(write-line (subseq "Hello World" 6))
(write (char "Hello World" 6))

Khi bạn thực thi mã, nó trả về kết quả sau:

11
World
#\W

Sắp xếp và Hợp nhất các chuỗi

Các sorthàm cho phép sắp xếp một chuỗi. Nó nhận một chuỗi (vectơ hoặc chuỗi) và một vị từ hai đối số và trả về một phiên bản được sắp xếp của chuỗi.

Các merge hàm nhận hai chuỗi và một vị từ và trả về một chuỗi được tạo ra bằng cách hợp nhất hai chuỗi, theo vị từ.

Example

Tạo một tệp mã nguồn mới có tên main.lisp và nhập mã sau vào đó.

;sorting the strings
(write (sort (vector "Amal" "Akbar" "Anthony") #'string<))
(terpri)

;merging the strings
(write (merge 'vector (vector "Rishi" "Zara" "Priyanka") 
   (vector "Anju" "Anuj" "Avni") #'string<))

Khi bạn thực thi mã, nó trả về kết quả sau:

#("Akbar" "Amal" "Anthony")
#("Anju" "Anuj" "Avni" "Rishi" "Zara" "Priyanka")

Đảo ngược một chuỗi

Các reverse hàm đảo ngược một chuỗi.

Ví dụ: Tạo một tệp mã nguồn mới có tên main.lisp và nhập mã sau vào đó.

(write-line (reverse "Are we not drawn onward, we few, drawn onward to new era"))

Khi bạn thực thi mã, nó trả về kết quả sau:

are wen ot drawno nward ,wef ew ,drawno nward ton ew erA

Nối các chuỗi

Hàm nối kết nối hai chuỗi. Đây là hàm trình tự chung và bạn phải cung cấp kiểu kết quả làm đối số đầu tiên.

Ví dụ: Tạo một tệp mã nguồn mới có tên main.lisp và nhập mã sau vào đó.

(write-line (concatenate 'string "Are we not drawn onward, " "we few, drawn onward to new era"))

Khi bạn thực thi mã, nó trả về kết quả sau:

Are we not drawn onward, we few, drawn onward to new era

Sequence là một kiểu dữ liệu trừu tượng trong LISP. Vectơ và danh sách là hai kiểu con cụ thể của kiểu dữ liệu này. Tất cả các chức năng được xác định trên kiểu dữ liệu tuần tự thực sự được áp dụng trên tất cả các vectơ và kiểu danh sách.

Trong phần này, chúng ta sẽ thảo luận về các hàm thường được sử dụng nhất trên chuỗi.

Trước khi bắt đầu các cách khác nhau để thao tác chuỗi (ví dụ, vectơ và danh sách), chúng ta hãy xem danh sách tất cả các hàm có sẵn.

Tạo trình tự

Hàm tạo chuỗi cho phép bạn tạo một chuỗi thuộc bất kỳ loại nào. Cú pháp cho hàm này là:

make-sequence sqtype sqsize &key :initial-element

Nó tạo ra một chuỗi kiểu sqtype và có độ dài là sqsize.

Bạn có thể tùy ý chỉ định một số giá trị bằng cách sử dụng đối số : initial-element , sau đó mỗi phần tử sẽ được khởi tạo thành giá trị này.

Ví dụ: Tạo một tệp mã nguồn mới có tên main.lisp và nhập mã sau vào đó.

(write (make-sequence '(vector float) 
   10 
   :initial-element 1.0))

Khi bạn thực thi mã, nó trả về kết quả sau:

#(1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0)

Các chức năng chung trên chuỗi

Sr.No. Mô tả chức năng
1

elt

Nó cho phép truy cập vào các phần tử riêng lẻ thông qua một chỉ mục số nguyên.

2

length

Nó trả về độ dài của một chuỗi.

3

subseq

Nó trả về một dãy con bằng cách trích xuất dãy con bắt đầu từ một chỉ mục cụ thể và tiếp tục đến một chỉ mục kết thúc cụ thể hoặc cuối dãy.

4

copy-seq

Nó trả về một chuỗi có chứa các phần tử giống như đối số của nó.

5

fill

Nó được sử dụng để đặt nhiều phần tử của một dãy thành một giá trị duy nhất.

6

replace

Nó có hai chuỗi và chuỗi đối số đầu tiên bị sửa đổi triệt để bằng cách sao chép các phần tử liên tiếp vào nó từ chuỗi đối số thứ hai.

7

count

Nó nhận một mục và một chuỗi và trả về số lần mục đó xuất hiện trong chuỗi.

số 8

reverse

Nó trả về một chuỗi chứa các phần tử giống nhau của đối số nhưng theo thứ tự ngược lại.

9

nreverse

Nó trả về cùng một chuỗi chứa các phần tử giống như chuỗi nhưng theo thứ tự ngược lại.

10

concatenate

Nó tạo ra một chuỗi mới có chứa sự ghép nối của bất kỳ số chuỗi nào.

11

position

Nó nhận một mục và một chuỗi và trả về chỉ mục của mục trong chuỗi hoặc nil.

12

find

Nó có một mục và một trình tự. Nó tìm thấy mục trong chuỗi và trả về, nếu không tìm thấy thì nó trả về nil.

13

sort

Nó nhận một chuỗi và một vị từ hai đối số và trả về một phiên bản đã được sắp xếp của chuỗi.

14

merge

Nó nhận hai chuỗi và một vị từ và trả về một chuỗi được tạo ra bằng cách hợp nhất hai chuỗi, theo vị từ.

15

map

Nó nhận một hàm n đối số và n dãy và trả về một dãy mới chứa kết quả của việc áp dụng hàm cho các phần tử tiếp theo của dãy.

16

some

Nó nhận một vị từ làm đối số và lặp qua chuỗi đối số và trả về giá trị không phải NIL đầu tiên được trả về bởi vị từ hoặc trả về false nếu vị từ không bao giờ được thỏa mãn.

17

every

Nó nhận một vị từ làm đối số và lặp qua chuỗi đối số, nó kết thúc, trả về false ngay khi vị từ bị lỗi. Nếu vị từ luôn được thỏa mãn, nó trả về true.

18

notany

Nó nhận một vị từ làm đối số và lặp qua chuỗi đối số và trả về false ngay khi vị từ được thỏa mãn hoặc đúng nếu nó không bao giờ như vậy.

19

notevery

Nó nhận một vị từ làm đối số và lặp qua chuỗi đối số và trả về true ngay khi vị từ bị lỗi hoặc sai nếu vị từ luôn được thỏa mãn.

20

reduce

Nó ánh xạ trên một dãy đơn, trước tiên áp dụng một hàm hai đối số cho hai phần tử đầu tiên của dãy rồi đến giá trị được trả về bởi hàm và các phần tử tiếp theo của dãy.

21

search

Nó tìm kiếm một chuỗi để xác định một hoặc nhiều phần tử đáp ứng một số thử nghiệm.

22

remove

Nó lấy một mục và một chuỗi và trả về chuỗi với các trường hợp của mục đã bị xóa.

23

delete

Thao tác này cũng nhận một mục và một chuỗi và trả về một chuỗi cùng loại với chuỗi đối số có các phần tử giống nhau ngoại trừ mục.

24

substitute

Nó lấy một mục mới, một mục hiện có và một chuỗi và trả về một chuỗi với các trường hợp của mục hiện có được thay thế bằng mục mới.

25

nsubstitute

Nó lấy một mục mới, một mục hiện có và một chuỗi và trả về cùng một chuỗi với các trường hợp của mục hiện có được thay thế bằng mục mới.

26

mismatch

Nó có hai chuỗi và trả về chỉ mục của cặp phần tử không khớp đầu tiên.

Đối số từ khóa của hàm theo trình tự chuẩn

Tranh luận Ý nghĩa Giá trị mặc định
:kiểm tra Nó là một hàm hai đối số được sử dụng để so sánh mục (hoặc giá trị được trích xuất bởi: key function) với phần tử. EQL
:Chìa khóa Hàm một đối số để trích xuất giá trị khóa từ phần tử chuỗi thực tế. NIL có nghĩa là sử dụng phần tử nguyên trạng. KHÔNG
:khởi đầu Chỉ mục bắt đầu (bao gồm) của dãy con. 0
:kết thúc Chỉ mục kết thúc (loại trừ) của dãy con. NIL cho biết kết thúc của chuỗi. KHÔNG
: từ cuối Nếu đúng, trình tự sẽ được duyệt theo thứ tự ngược lại, từ cuối đến đầu. KHÔNG
:đếm Số cho biết số phần tử cần loại bỏ hoặc thay thế hoặc NIL để chỉ ra tất cả (chỉ XÓA và GỬI). KHÔNG

Chúng ta vừa thảo luận về các hàm và từ khóa khác nhau được sử dụng làm đối số trong các hàm này hoạt động trên chuỗi. Trong các phần tiếp theo, chúng ta sẽ xem cách sử dụng các hàm này bằng các ví dụ.

Tìm độ dài và yếu tố

Các length hàm trả về độ dài của một chuỗi và elt hàm cho phép bạn truy cập các phần tử riêng lẻ bằng cách sử dụng một chỉ mục số nguyên.

Thí dụ

Tạo một tệp mã nguồn mới có tên main.lisp và nhập mã sau vào đó.

(setq x (vector 'a 'b 'c 'd 'e))
(write (length x))
(terpri)
(write (elt x 3))

Khi bạn thực thi mã, nó trả về kết quả sau:

5
D

Sửa đổi chuỗi

Một số hàm tuần tự cho phép lặp qua trình tự và thực hiện một số hoạt động như tìm kiếm, loại bỏ, đếm hoặc lọc các phần tử cụ thể mà không cần viết các vòng lặp rõ ràng.

Ví dụ sau đây chứng minh điều này -

ví dụ 1

Tạo một tệp mã nguồn mới có tên main.lisp và nhập mã sau vào đó.

(write (count 7 '(1 5 6 7 8 9 2 7 3 4 5)))
(terpri)
(write (remove 5 '(1 5 6 7 8 9 2 7 3 4 5)))
(terpri)
(write (delete 5 '(1 5 6 7 8 9 2 7 3 4 5)))
(terpri)
(write (substitute 10 7 '(1 5 6 7 8 9 2 7 3 4 5)))
(terpri)
(write (find 7 '(1 5 6 7 8 9 2 7 3 4 5)))
(terpri)
(write (position 5 '(1 5 6 7 8 9 2 7 3 4 5)))

Khi bạn thực thi mã, nó trả về kết quả sau:

2
(1 6 7 8 9 2 7 3 4)
(1 6 7 8 9 2 7 3 4)
(1 5 6 10 8 9 2 10 3 4 5)
7
1

Ví dụ 2

Tạo một tệp mã nguồn mới có tên main.lisp và nhập mã sau vào đó.

(write (delete-if #'oddp '(1 5 6 7 8 9 2 7 3 4 5)))
(terpri)
(write (delete-if #'evenp '(1 5 6 7 8 9 2 7 3 4 5)))
(terpri)
(write (remove-if #'evenp '(1 5 6 7 8 9 2 7 3 4 5) :count 1 :from-end t))
(terpri)
(setq x (vector 'a 'b 'c 'd 'e 'f 'g))
(fill x 'p :start 1 :end 4)
(write x)

Khi bạn thực thi mã, nó trả về kết quả sau:

(6 8 2 4)
(1 5 7 9 7 3 5)
(1 5 6 7 8 9 2 7 3 5)
#(A P P P E F G)

Sắp xếp và hợp nhất chuỗi

Các hàm sắp xếp nhận một chuỗi và một vị từ hai đối số và trả về một phiên bản đã được sắp xếp của chuỗi.

ví dụ 1

Tạo một tệp mã nguồn mới có tên main.lisp và nhập mã sau vào đó.

(write (sort '(2 4 7 3 9 1 5 4 6 3 8) #'<))
(terpri)
(write (sort '(2 4 7 3 9 1 5 4 6 3 8) #'>))
(terpri)

Khi bạn thực thi mã, nó trả về kết quả sau:

(1 2 3 3 4 4 5 6 7 8 9)
(9 8 7 6 5 4 4 3 3 2 1)

Ví dụ 2

Tạo một tệp mã nguồn mới có tên main.lisp và nhập mã sau vào đó.

(write (merge 'vector #(1 3 5) #(2 4 6) #'<))
(terpri)
(write (merge 'list #(1 3 5) #(2 4 6) #'<))
(terpri)

Khi bạn thực thi mã, nó trả về kết quả sau:

#(1 2 3 4 5 6)
(1 2 3 4 5 6)

Dự đoán trình tự

Các hàm every, some, notany và notevery được gọi là các vị từ trình tự.

Các hàm này lặp qua các chuỗi và kiểm tra vị từ Boolean.

Tất cả các hàm này nhận một vị từ làm đối số đầu tiên và các đối số còn lại là chuỗi.

Thí dụ

Tạo một tệp mã nguồn mới có tên main.lisp và nhập mã sau vào đó.

(write (every #'evenp #(2 4 6 8 10)))
(terpri)
(write (some #'evenp #(2 4 6 8 10 13 14)))
(terpri)
(write (every #'evenp #(2 4 6 8 10 13 14)))
(terpri)
(write (notany #'evenp #(2 4 6 8 10)))
(terpri)
(write (notevery #'evenp #(2 4 6 8 10 13 14)))
(terpri)

Khi bạn thực thi mã, nó trả về kết quả sau:

T
T
NIL
NIL
T

Lập bản đồ trình tự

Chúng ta đã thảo luận về các chức năng ánh xạ. Tương tự như vậymap hàm cho phép bạn áp dụng một hàm cho các phần tử tiếp theo của một hoặc nhiều chuỗi.

Các map hàm nhận một hàm n đối số và n dãy và trả về một dãy mới sau khi áp dụng hàm cho các phần tử tiếp theo của dãy.

Thí dụ

Tạo một tệp mã nguồn mới có tên main.lisp và nhập mã sau vào đó.

(write (map 'vector #'* #(2 3 4 5) #(3 5 4 8)))

Khi bạn thực thi mã, nó trả về kết quả sau:

#(6 15 16 40)

Danh sách từng là cấu trúc dữ liệu tổng hợp quan trọng nhất và chính trong LISP truyền thống. LISP chung của ngày hôm nay cung cấp các cấu trúc dữ liệu khác như vectơ, bảng băm, các lớp hoặc cấu trúc.

Danh sách là danh sách liên kết đơn. Trong LISP, danh sách được xây dựng như một chuỗi của cấu trúc bản ghi đơn giản có têncons kết nối mọi người.

Cấu trúc bản ghi khuyết điểm

A cons là một cấu trúc bản ghi có chứa hai thành phần được gọi là carcdr.

Ô khuyết điểm hoặc ô khuyết điểm là các đối tượng là các cặp giá trị được tạo bằng cách sử dụng hàm cons.

Các conshàm nhận hai đối số và trả về một ô khuyết điểm mới chứa hai giá trị. Các giá trị này có thể là tham chiếu đến bất kỳ loại đối tượng nào.

Nếu giá trị thứ hai không phải là nil, hoặc một ô khuyết điểm khác, thì các giá trị được in dưới dạng một cặp dấu chấm được đặt trong dấu ngoặc đơn.

Hai giá trị trong ô khuyết điểm được gọi là carcdr. Các car hàm được sử dụng để truy cập giá trị đầu tiên và cdr hàm được sử dụng để truy cập giá trị thứ hai.

Thí dụ

Tạo một tệp mã nguồn mới có tên main.lisp và nhập mã sau vào đó.

(write (cons 1 2))
(terpri)
(write (cons 'a 'b))
(terpri)
(write (cons 1 nil))
(terpri)
(write (cons 1 (cons 2 nil)))
(terpri)
(write (cons 1 (cons 2 (cons 3 nil))))
(terpri)
(write (cons 'a (cons 'b (cons 'c nil))))
(terpri)
(write ( car (cons 'a (cons 'b (cons 'c nil)))))
(terpri)
(write ( cdr (cons 'a (cons 'b (cons 'c nil)))))

Khi bạn thực thi mã, nó trả về kết quả sau:

(1 . 2)
(A . B)
(1)
(1 2)
(1 2 3)
(A B C)
A
(B C)

Ví dụ trên cho thấy cách các cấu trúc khuyết điểm có thể được sử dụng để tạo một danh sách liên kết duy nhất, ví dụ: danh sách (ABC) bao gồm ba ô khuyết điểm được liên kết với nhau bằng cdrs của chúng .

Theo sơ đồ, nó có thể được biểu thị là -

Danh sách trong LISP

Mặc dù các ô khuyết điểm có thể được sử dụng để tạo danh sách, tuy nhiên, việc xây dựng một danh sách ngoài consgọi hàm không thể là giải pháp tốt nhất. Cáclist hàm được sử dụng để tạo danh sách trong LISP.

Hàm danh sách có thể nhận bất kỳ số lượng đối số nào và vì nó là một hàm, nó sẽ đánh giá các đối số của nó.

Các firstresthàm cung cấp phần tử đầu tiên và phần còn lại của danh sách. Các ví dụ sau đây chứng minh các khái niệm.

ví dụ 1

Tạo một tệp mã nguồn mới có tên main.lisp và nhập mã sau vào đó.

(write (list 1 2))
(terpri)
(write (list 'a 'b))
(terpri)
(write (list 1 nil))
(terpri)
(write (list 1 2 3))
(terpri)
(write (list 'a 'b 'c))
(terpri)
(write (list 3 4 'a (car '(b . c)) (* 4 -2)))
(terpri)
(write (list (list 'a 'b) (list 'c 'd 'e)))

Khi bạn thực thi mã, nó trả về kết quả sau:

(1 2)
(A B)
(1 NIL)
(1 2 3)
(A B C)
(3 4 A B -8)
((A B) (C D E))

Ví dụ 2

Tạo một tệp mã nguồn mới có tên main.lisp và nhập mã sau vào đó.

(defun my-library (title author rating availability)
   (list :title title :author author :rating rating :availabilty availability)
)

(write (getf (my-library "Hunger Game" "Collins" 9 t) :title))

Khi bạn thực thi mã, nó trả về kết quả sau:

"Hunger Game"

Liệt kê các chức năng thao tác

Bảng sau cung cấp một số hàm thao tác danh sách thường được sử dụng.

Sr.No. Mô tả chức năng
1

car

Nó nhận một danh sách làm đối số và trả về phần tử đầu tiên của nó.

2

cdr

Nó nhận một danh sách làm đối số và trả về một danh sách không có phần tử đầu tiên

3

cons

Nó nhận hai đối số, một phần tử và một danh sách và trả về một danh sách có phần tử được chèn ở vị trí đầu tiên.

4

list

Nó nhận bất kỳ số lượng đối số nào và trả về một danh sách với các đối số là phần tử thành viên của danh sách.

5

append

Nó hợp nhất hai hoặc nhiều danh sách thành một.

6

last

Nó nhận một danh sách và trả về một danh sách có chứa phần tử cuối cùng.

7

member

Nó cần hai đối số trong đó đối số thứ hai phải là một danh sách, nếu đối số đầu tiên là một thành viên của đối số thứ hai, và sau đó nó trả về phần còn lại của danh sách bắt đầu bằng đối số đầu tiên.

số 8

reverse

Nó lấy một danh sách và trả về một danh sách với các phần tử trên cùng theo thứ tự ngược lại.

Xin lưu ý rằng tất cả các hàm trình tự đều có thể áp dụng cho danh sách.

Ví dụ 3

Tạo một tệp mã nguồn mới có tên main.lisp và nhập mã sau vào đó.

(write (car '(a b c d e f)))
(terpri)
(write (cdr '(a b c d e f)))
(terpri)
(write (cons 'a '(b c)))
(terpri)
(write (list 'a '(b c) '(e f)))
(terpri)
(write (append '(b c) '(e f) '(p q) '() '(g)))
(terpri)
(write (last '(a b c d (e f))))
(terpri)
(write (reverse '(a b c d (e f))))

Khi bạn thực thi mã, nó trả về kết quả sau:

A
(B C D E F)
(A B C)
(A (B C) (E F))
(B C E F P Q G)
((E F))
((E F) D C B A)

Sự kết hợp của chức năng xe hơi và cdr

Các carcdr và sự kết hợp của chúng cho phép trích xuất bất kỳ phần tử / thành viên cụ thể nào của danh sách.

Tuy nhiên, chuỗi các hàm car và cdr có thể được viết tắt bằng cách ghép chữ a cho car và d cho cdr trong các chữ c và r.

Ví dụ, chúng ta có thể viết cadadr để viết tắt chuỗi các lệnh gọi hàm - car cdr car cdr.

Do đó, (cadadr '(a (cd) (efg))) sẽ trả về d

Ví dụ 4

Tạo một tệp mã nguồn mới có tên main.lisp và nhập mã sau vào đó.

(write (cadadr '(a (c d) (e f g))))
(terpri)
(write (caar (list (list 'a 'b) 'c)))   
(terpri)
(write (cadr (list (list 1 2) (list 3 4))))
(terpri)

Khi bạn thực thi mã, nó trả về kết quả sau:

D
A
(3 4)

Trong LISP, một biểu tượng là tên đại diện cho các đối tượng dữ liệu và điều thú vị là nó cũng là một đối tượng dữ liệu.

Điều làm cho các biểu tượng trở nên đặc biệt là chúng có một thành phần được gọi là property list, hoặc là plist.

Danh sách tài sản

LISP cho phép bạn gán thuộc tính cho các ký hiệu. Ví dụ, chúng ta có một đối tượng 'người'. Chúng tôi muốn đối tượng 'person' này có các thuộc tính như tên, giới tính, chiều cao, cân nặng, địa chỉ, nghề nghiệp, v.v. Một thuộc tính giống như tên thuộc tính.

Một danh sách thuộc tính được triển khai dưới dạng danh sách có số phần tử chẵn (có thể bằng không). Mỗi cặp phần tử trong danh sách tạo thành một mục nhập; mục đầu tiên làindicator, và thứ hai là value.

Khi một biểu tượng được tạo, danh sách thuộc tính của nó lúc đầu trống. Thuộc tính được tạo bằng cách sử dụngget trong một setf hình thức.

Ví dụ: các câu lệnh sau đây cho phép chúng tôi gán tên thuộc tính, tác giả và nhà xuất bản và các giá trị tương ứng, cho một đối tượng có tên (ký hiệu) 'book'.

ví dụ 1

Tạo một tệp mã nguồn mới có tên main.lisp và nhập mã sau vào đó.

(write (setf (get 'books'title) '(Gone with the Wind)))
(terpri)
(write (setf (get 'books 'author) '(Margaret Michel)))
(terpri)
(write (setf (get 'books 'publisher) '(Warner Books)))

Khi bạn thực thi mã, nó trả về kết quả sau:

(GONE WITH THE WIND)
(MARGARET MICHEL)
(WARNER BOOKS)

Các chức năng danh sách thuộc tính khác nhau cho phép bạn gán các thuộc tính cũng như truy xuất, thay thế hoặc xóa các thuộc tính của một biểu tượng.

Các gethàm trả về danh sách thuộc tính của biểu tượng cho một chỉ số nhất định. Nó có cú pháp sau:

get symbol indicator &optional default

Các gethàm tìm kiếm danh sách thuộc tính của ký hiệu đã cho cho chỉ số được chỉ định, nếu được tìm thấy thì nó trả về giá trị tương ứng; nếu không thì giá trị mặc định được trả về (hoặc nil, nếu giá trị mặc định không được chỉ định).

Ví dụ 2

Tạo một tệp mã nguồn mới có tên main.lisp và nhập mã sau vào đó.

(setf (get 'books 'title) '(Gone with the Wind))
(setf (get 'books 'author) '(Margaret Micheal))
(setf (get 'books 'publisher) '(Warner Books))

(write (get 'books 'title))
(terpri)
(write (get 'books 'author))
(terpri)
(write (get 'books 'publisher))

Khi bạn thực thi mã, nó trả về kết quả sau:

(GONE WITH THE WIND)
(MARGARET MICHEAL)
(WARNER BOOKS)

Các symbol-plist cho phép bạn xem tất cả các thuộc tính của một biểu tượng.

Ví dụ 3

Tạo một tệp mã nguồn mới có tên main.lisp và nhập mã sau vào đó.

(setf (get 'annie 'age) 43)
(setf (get 'annie 'job) 'accountant)
(setf (get 'annie 'sex) 'female)
(setf (get 'annie 'children) 3)

(terpri)
(write (symbol-plist 'annie))

Khi bạn thực thi mã, nó trả về kết quả sau:

(CHILDREN 3 SEX FEMALE JOB ACCOUNTANT AGE 43)

Các remprop hàm xóa thuộc tính được chỉ định khỏi một biểu tượng.

Ví dụ 4

Tạo một tệp mã nguồn mới có tên main.lisp và nhập mã sau vào đó.

(setf (get 'annie 'age) 43)
(setf (get 'annie 'job) 'accountant)
(setf (get 'annie 'sex) 'female)
(setf (get 'annie 'children) 3)

(terpri)
(write (symbol-plist 'annie))
(remprop 'annie 'age)
(terpri)
(write (symbol-plist 'annie))

Khi bạn thực thi mã, nó trả về kết quả sau:

(CHILDREN 3 SEX FEMALE JOB ACCOUNTANT AGE 43)
(CHILDREN 3 SEX FEMALE JOB ACCOUNTANT)

Vectơ là mảng một chiều, do đó là một kiểu con của mảng. Vectơ và danh sách được gọi chung là dãy. Do đó, tất cả các hàm chung chuỗi và các hàm mảng mà chúng ta đã thảo luận cho đến nay, đều hoạt động trên vectơ.

Tạo vectơ

Hàm vectơ cho phép bạn tạo vectơ có kích thước cố định với các giá trị cụ thể. Nó nhận bất kỳ số lượng đối số nào và trả về một vectơ chứa các đối số đó.

ví dụ 1

Tạo một tệp mã nguồn mới có tên main.lisp và nhập mã sau vào đó.

(setf v1 (vector 1 2 3 4 5))
(setf v2 #(a b c d e))
(setf v3 (vector 'p 'q 'r 's 't))

(write v1)
(terpri)
(write v2)
(terpri)
(write v3)

Khi bạn thực thi mã, nó trả về kết quả sau:

#(1 2 3 4 5)
#(A B C D E)
#(P Q R S T)

Xin lưu ý rằng LISP sử dụng cú pháp # (...) làm ký hiệu chữ cho vectơ. Bạn có thể sử dụng cú pháp # (...) này để tạo và bao gồm các vectơ chữ trong mã của mình.

Tuy nhiên, đây là các vectơ theo nghĩa đen, vì vậy việc sửa đổi chúng không được định nghĩa trong LISP. Do đó, để lập trình, bạn nên luôn sử dụngvector chức năng, hoặc chức năng tổng quát hơn make-array để tạo vectơ mà bạn định sửa đổi.

Các make-arrayhàm là cách chung chung hơn để tạo một vectơ. Bạn có thể truy cập các phần tử vectơ bằng cách sử dụngaref chức năng.

Ví dụ 2

Tạo một tệp mã nguồn mới có tên main.lisp và nhập mã sau vào đó.

(setq a (make-array 5 :initial-element 0))
(setq b (make-array 5 :initial-element 2))

(dotimes (i 5)
   (setf (aref a i) i))
   
(write a)
(terpri)
(write b)
(terpri)

Khi bạn thực thi mã, nó trả về kết quả sau:

#(0 1 2 3 4)
#(2 2 2 2 2)

Điền con trỏ

Các make-array cho phép bạn tạo một vectơ có thể thay đổi kích thước.

Các fill-pointerđối số của hàm theo dõi số phần tử thực sự được lưu trữ trong vectơ. Đó là chỉ số của vị trí tiếp theo được điền khi bạn thêm một phần tử vào vectơ.

Các vector-pushcho phép bạn thêm một phần tử vào cuối một vectơ có thể thay đổi kích thước. Nó làm tăng con trỏ điền lên 1.

Các vector-pop hàm trả về mục được đẩy gần đây nhất và giảm con trỏ điền đi 1.

Thí dụ

Tạo một tệp mã nguồn mới có tên main.lisp và nhập mã sau vào đó.

(setq a (make-array 5 :fill-pointer 0))
(write a)

(vector-push 'a a)
(vector-push 'b a)
(vector-push 'c a)

(terpri)
(write a)
(terpri)

(vector-push 'd a)
(vector-push 'e a)

;this will not be entered as the vector limit is 5
(vector-push 'f a)

(write a)
(terpri)

(vector-pop a)
(vector-pop a)
(vector-pop a)

(write a)

Khi bạn thực thi mã, nó trả về kết quả sau:

#()
#(A B C)
#(A B C D E)
#(A B)

Vectơ là chuỗi, tất cả các hàm chuỗi đều có thể áp dụng cho vectơ. Vui lòng tham khảo chương dãy số, để biết hàm vectơ.

Lisp chung không cung cấp kiểu dữ liệu tập hợp. Tuy nhiên, nó cung cấp một số chức năng cho phép thực hiện các hoạt động thiết lập trên một danh sách.

Bạn có thể thêm, bớt và tìm kiếm các mục trong danh sách, dựa trên các tiêu chí khác nhau. Bạn cũng có thể thực hiện các hoạt động tập hợp khác nhau như: liên hợp, giao điểm và đặt chênh lệch.

Bộ triển khai trong LISP

Các tập hợp, giống như danh sách thường được triển khai theo các ô khuyết điểm. Tuy nhiên, vì lý do này, các hoạt động tập hợp ngày càng kém hiệu quả hơn khi tập hợp càng lớn.

Các adjoinchức năng cho phép bạn thiết lập một tập hợp. Nó lấy một mục và một danh sách đại diện cho một tập hợp và trả về một danh sách đại diện cho tập hợp chứa mục và tất cả các mục trong tập hợp ban đầu.

Các adjoinđầu tiên hàm tìm kiếm mục trong danh sách đã cho, nếu tìm thấy nó, thì nó sẽ trả về danh sách ban đầu; nếu không, nó tạo ra một ô khuyết điểm mới vớicar như mặt hàng và cdr trỏ đến danh sách ban đầu và trả về danh sách mới này.

Các adjoin chức năng cũng mất :key:testđối số từ khóa. Các đối số này được sử dụng để kiểm tra xem mục có trong danh sách gốc hay không.

Vì, hàm adjoin không sửa đổi danh sách ban đầu, để thực hiện thay đổi trong chính danh sách, bạn phải gán giá trị được trả về bởi hàm adjoin vào danh sách ban đầu hoặc bạn có thể sử dụng macro pushnew để thêm một mục vào tập hợp.

Thí dụ

Tạo một tệp mã nguồn mới có tên main.lisp và nhập mã sau vào đó.

; creating myset as an empty list
(defparameter *myset* ())
(adjoin 1 *myset*)
(adjoin 2 *myset*)

; adjoin did not change the original set
;so it remains same
(write *myset*)
(terpri)
(setf *myset* (adjoin 1 *myset*))
(setf *myset* (adjoin 2 *myset*))

;now the original set is changed
(write *myset*)
(terpri)

;adding an existing value
(pushnew 2 *myset*)

;no duplicate allowed
(write *myset*)
(terpri)

;pushing a new value
(pushnew 3 *myset*)
(write *myset*)
(terpri)

Khi bạn thực thi mã, nó trả về kết quả sau:

NIL
(2 1)
(2 1)
(3 2 1)

Kiểm tra tư cách thành viên

Nhóm chức năng thành viên cho phép bạn kiểm tra xem một phần tử có phải là thành viên của một tập hợp hay không.

Sau đây là cú pháp của các hàm này:

member item list &key :test :test-not :key 
member-if predicate list &key :key 
member-if-not predicate list &key :key

Các hàm này tìm kiếm trong danh sách nhất định cho một mục nhất định đáp ứng thử nghiệm. Nếu không tìm thấy mục nào như vậy, thì các hàm trả vềnil. Nếu không, phần đuôi của danh sách có phần tử là phần tử đầu tiên được trả về.

Việc tìm kiếm chỉ được thực hiện ở cấp cao nhất.

Các hàm này có thể được sử dụng làm vị từ.

Thí dụ

Tạo một tệp mã nguồn mới có tên main.lisp và nhập mã sau vào đó.

(write (member 'zara '(ayan abdul zara riyan nuha)))
(terpri)
(write (member-if #'evenp '(3 7 2 5/3 'a)))
(terpri)
(write (member-if-not #'numberp '(3 7 2 5/3 'a 'b 'c)))

Khi bạn thực thi mã, nó trả về kết quả sau:

(ZARA RIYAN NUHA)
(2 5/3 'A)
('A 'B 'C)

Đặt Union

Nhóm hàm liên hợp cho phép bạn thực hiện liên hợp thiết lập trên hai danh sách được cung cấp làm đối số cho các hàm này trên cơ sở kiểm tra.

Sau đây là cú pháp của các hàm này:

union list1 list2 &key :test :test-not :key 
nunion list1 list2 &key :test :test-not :key

Các unionhàm nhận hai danh sách và trả về một danh sách mới chứa tất cả các phần tử có trong một trong hai danh sách. Nếu có sự trùng lặp, thì chỉ một bản sao của thành viên được giữ lại trong danh sách trả về.

Các nunion hàm thực hiện cùng một hoạt động nhưng có thể phá hủy danh sách đối số.

Thí dụ

Tạo một tệp mã nguồn mới có tên main.lisp và nhập mã sau vào đó.

(setq set1 (union '(a b c) '(c d e)))
(setq set2 (union '(#(a b) #(5 6 7) #(f h)) 
   '(#(5 6 7) #(a b) #(g h)) :test-not #'mismatch)
)
       
(setq set3 (union '(#(a b) #(5 6 7) #(f h)) 
   '(#(5 6 7) #(a b) #(g h)))
)
(write set1)
(terpri)
(write set2)
(terpri)
(write set3)

Khi bạn thực thi mã, nó trả về kết quả sau:

(A B C D E)
(#(F H) #(5 6 7) #(A B) #(G H))
(#(A B) #(5 6 7) #(F H) #(5 6 7) #(A B) #(G H))

Xin lưu ý

Chức năng liên hiệp không hoạt động như mong đợi nếu không có :test-not #'mismatchđối số cho danh sách ba vectơ. Điều này là do, danh sách được tạo bằng các ô khuyết điểm và mặc dù các giá trị trông giống nhau đối với chúng ta, nhưngcdrmột phần của các ô không khớp, vì vậy chúng không hoàn toàn giống với trình thông dịch / trình biên dịch LISP. Đây là lý do; thực hiện các tập hợp lớn không được khuyên sử dụng danh sách. Nó hoạt động tốt cho các bộ nhỏ.

Đặt giao lộ

Nhóm giao nhau của các hàm cho phép bạn thực hiện giao nhau trên hai danh sách được cung cấp làm đối số cho các hàm này trên cơ sở kiểm tra.

Sau đây là cú pháp của các hàm này:

intersection list1 list2 &key :test :test-not :key 
nintersection list1 list2 &key :test :test-not :key

Các hàm này nhận hai danh sách và trả về một danh sách mới chứa tất cả các phần tử có trong cả hai danh sách đối số. Nếu một trong hai danh sách có các mục nhập trùng lặp, các mục nhập thừa có thể xuất hiện hoặc không xuất hiện trong kết quả.

Thí dụ

Tạo một tệp mã nguồn mới có tên main.lisp và nhập mã sau vào đó.

(setq set1 (intersection '(a b c) '(c d e)))
(setq set2 (intersection '(#(a b) #(5 6 7) #(f h)) 
   '(#(5 6 7) #(a b) #(g h)) :test-not #'mismatch)
)
       
(setq set3 (intersection '(#(a b) #(5 6 7) #(f h)) 
   '(#(5 6 7) #(a b) #(g h)))
)
(write set1)
(terpri)
(write set2)
(terpri)
(write set3)

Khi bạn thực thi mã, nó trả về kết quả sau:

(C)
(#(A B) #(5 6 7))
NIL

Hàm giao nhau là phiên bản hủy của giao điểm, tức là, nó có thể hủy danh sách ban đầu.

Đặt sự khác biệt

Nhóm chức năng khác biệt tập hợp cho phép bạn thực hiện chênh lệch tập hợp trên hai danh sách được cung cấp làm đối số cho các hàm này trên cơ sở kiểm tra.

Sau đây là cú pháp của các hàm này:

set-difference list1 list2 &key :test :test-not :key 
nset-difference list1 list2 &key :test :test-not :key

Hàm set-difference trả về danh sách các phần tử của danh sách đầu tiên không xuất hiện trong danh sách thứ hai.

Thí dụ

Tạo một tệp mã nguồn mới có tên main.lisp và nhập mã sau vào đó.

(setq set1 (set-difference '(a b c) '(c d e)))
(setq set2 (set-difference '(#(a b) #(5 6 7) #(f h)) 
   '(#(5 6 7) #(a b) #(g h)) :test-not #'mismatch)
)
(setq set3 (set-difference '(#(a b) #(5 6 7) #(f h)) 
   '(#(5 6 7) #(a b) #(g h)))
)
(write set1)
(terpri)
(write set2)
(terpri)
(write set3)

Khi bạn thực thi mã, nó trả về kết quả sau:

(A B)
(#(F H))
(#(A B) #(5 6 7) #(F H))

Bạn có thể xây dựng cấu trúc dữ liệu cây từ các ô khuyết điểm, dưới dạng danh sách các danh sách.

Để triển khai cấu trúc cây, bạn sẽ phải thiết kế các chức năng sẽ đi qua các ô khuyết điểm, theo thứ tự cụ thể, ví dụ, thứ tự trước, thứ tự và sau cho cây nhị phân.

Cây dưới dạng Danh sách Danh sách

Chúng ta hãy xem xét một cấu trúc cây được tạo thành từ các ô khuyết điểm tạo thành danh sách các danh sách sau:

((1 2) (3 4) (5 6)).

Theo sơ đồ, nó có thể được biểu thị là -

Các chức năng của cây trong LISP

Mặc dù hầu hết bạn sẽ cần phải viết các chức năng cây của riêng mình theo nhu cầu cụ thể của bạn, nhưng LISP cung cấp một số chức năng cây mà bạn có thể sử dụng.

Ngoài tất cả các hàm trong danh sách, các hàm sau hoạt động đặc biệt trên cấu trúc cây:

Sr.No. Mô tả chức năng
1

copy-tree x & vecp tùy chọn

Nó trả về một bản sao của cây ô khuyết điểm x. Nó sao chép đệ quy cả hướng xe và cdr. Nếu x không phải là ô khuyết điểm, hàm chỉ trả về x không thay đổi. Nếu đối số vecp tùy chọn là true, hàm này sẽ sao chép các vectơ (một cách đệ quy) cũng như các ô khuyết điểm.

2

tree-equal xy & key: test: test-not: key

Nó so sánh hai cây ô khuyết điểm. Nếu x và y đều là ô khuyết điểm, thì ô tô và mã của chúng được so sánh một cách đệ quy. Nếu cả x và y đều không phải là ô khuyết điểm, chúng được so sánh bằng eql hoặc theo thử nghiệm được chỉ định. Hàm: key, nếu được chỉ định, được áp dụng cho các phần tử của cả hai cây.

3

subst cây cũ mới & key: test: test-not: key

Nó thay thế các lần xuất hiện của mục cũ đã cho bằng mục mới , trong cây , là một cây gồm các ô khuyết điểm.

4

nsubst cây cũ mới & key: test: test-not: key

Nó hoạt động giống như subst, nhưng nó phá hủy cây gốc.

5

sublis cây alist & key: test: test-not: key

Nó hoạt động như subst, ngoại trừ việc nó có một danh sách liên kết alist các cặp cũ-mới. Mỗi phần tử của cây (sau khi áp dụng: chức năng chính, nếu có), được so sánh với các ô tô của alist; nếu nó khớp, nó được thay thế bằng cdr tương ứng.

6

nsublis cây alist & key: test: test-not: key

Nó hoạt động giống như sublis, nhưng là một phiên bản hủy diệt.

ví dụ 1

Tạo một tệp mã nguồn mới có tên main.lisp và nhập mã sau vào đó.

(setq lst (list '(1 2) '(3 4) '(5 6)))
(setq mylst (copy-list lst))
(setq tr (copy-tree lst))

(write lst)
(terpri)
(write mylst)
(terpri)
(write tr)

Khi bạn thực thi mã, nó trả về kết quả sau:

((1 2) (3 4) (5 6))
((1 2) (3 4) (5 6))
((1 2) (3 4) (5 6))

Ví dụ 2

Tạo một tệp mã nguồn mới có tên main.lisp và nhập mã sau vào đó.

(setq tr '((1 2 (3 4 5) ((7 8) (7 8 9)))))
(write tr)
(setq trs (subst 7 1 tr))
(terpri)
(write trs)

Khi bạn thực thi mã, nó trả về kết quả sau:

((1 2 (3 4 5) ((7 8) (7 8 9))))
((7 2 (3 4 5) ((7 8) (7 8 9))))

Xây dựng cây của riêng bạn

Hãy để chúng tôi cố gắng xây dựng cây của riêng mình, sử dụng các chức năng danh sách có sẵn trong LISP.

Trước tiên, hãy tạo một nút mới chứa một số dữ liệu

(defun make-tree (item)
   "it creates a new node with item."
   (cons (cons item nil) nil)
)

Tiếp theo, chúng ta hãy thêm một nút con vào cây - nó sẽ lấy hai nút cây và thêm cây thứ hai làm nút con của cây đầu tiên.

(defun add-child (tree child)
   (setf (car tree) (append (car tree) child))
   tree)

Hàm này sẽ trả về con đầu tiên của một cây nhất định - nó sẽ lấy một nút cây và trả về con đầu tiên của nút đó, hoặc nil, nếu nút này không có bất kỳ nút con nào.

(defun first-child (tree)
   (if (null tree)
      nil
      (cdr (car tree))
   )
)

Hàm này sẽ trả về nút anh em tiếp theo của một nút nhất định - nó lấy một nút cây làm đối số và trả về một tham chiếu đến nút anh em tiếp theo, hoặc nil, nếu nút không có nút nào.

(defun next-sibling (tree)
   (cdr tree)
)

Cuối cùng, chúng ta cần một hàm để trả về thông tin trong một nút -

(defun data (tree)
   (car (car tree))
)

Thí dụ

Ví dụ này sử dụng các chức năng trên -

Tạo một tệp mã nguồn mới có tên main.lisp và nhập mã sau vào đó.

(defun make-tree (item)
   "it creates a new node with item."
   (cons (cons item nil) nil)
)
(defun first-child (tree)
   (if (null tree)
      nil
      (cdr (car tree))
   )
)

(defun next-sibling (tree)
   (cdr tree)
)
(defun data (tree)
   (car (car tree))
)
(defun add-child (tree child)
   (setf (car tree) (append (car tree) child))
   tree
)

(setq tr '((1 2 (3 4 5) ((7 8) (7 8 9)))))
(setq mytree (make-tree 10))

(write (data mytree))
(terpri)
(write (first-child tr))
(terpri)
(setq newtree (add-child tr mytree))
(terpri)
(write newtree)

Khi bạn thực thi mã, nó trả về kết quả sau:

10
(2 (3 4 5) ((7 8) (7 8 9)))

((1 2 (3 4 5) ((7 8) (7 8 9)) (10)))

Cấu trúc dữ liệu bảng băm đại diện cho một tập hợp key-and-valuecác cặp được tổ chức dựa trên mã băm của khóa. Nó sử dụng khóa để truy cập các phần tử trong bộ sưu tập.

Bảng băm được sử dụng khi bạn cần truy cập các phần tử bằng cách sử dụng khóa và bạn có thể xác định một giá trị khóa hữu ích. Mỗi mục trong bảng băm có một cặp khóa / giá trị. Chìa khóa được sử dụng để truy cập các mục trong bộ sưu tập.

Tạo bảng băm trong LISP

Trong Common LISP, bảng băm là một tập hợp có mục đích chung. Bạn có thể sử dụng các đối tượng tùy ý làm khóa hoặc các chỉ mục.

Khi bạn lưu trữ một giá trị trong bảng băm, bạn tạo một cặp khóa-giá trị và lưu trữ nó dưới khóa đó. Sau đó, bạn có thể lấy giá trị từ bảng băm bằng cách sử dụng cùng một khóa. Mỗi khóa ánh xạ đến một giá trị duy nhất, mặc dù bạn có thể lưu trữ một giá trị mới trong một khóa.

Bảng băm, trong LISP, có thể được phân loại thành ba loại, dựa trên cách các khóa có thể được so sánh - eq, eql hoặc bằng. Nếu bảng băm được băm trên các đối tượng LISP thì các khóa sẽ được so sánh với eq hoặc eql. Nếu bảng băm băm trên cấu trúc cây, thì nó sẽ được so sánh bằng cách sử dụng bằng nhau.

Các make-hash-tablehàm được sử dụng để tạo bảng băm. Cú pháp cho hàm này là:

make-hash-table &key :test :size :rehash-size :rehash-threshold

Ở đâu -

  • Các key đối số cung cấp khóa.

  • Các :testđối số xác định cách các khóa được so sánh - nó phải có một trong ba giá trị # 'eq, #' eql hoặc # 'bằng nhau hoặc một trong ba ký hiệu eq, eql hoặc bằng. Nếu không được chỉ định, eql được giả định.

  • Các :sizeđối số đặt kích thước ban đầu của bảng băm. Đây phải là một số nguyên lớn hơn 0.

  • Các :rehash-sizeđối số chỉ định mức tăng kích thước của bảng băm khi nó đầy. Đây có thể là một số nguyên lớn hơn 0, là số mục nhập cần thêm hoặc nó có thể là một số dấu phẩy động lớn hơn 1, là tỷ lệ của kích thước mới với kích thước cũ. Giá trị mặc định cho đối số này phụ thuộc vào việc triển khai.

  • Các :rehash-thresholdđối số chỉ định mức độ đầy đủ mà bảng băm có thể nhận được trước khi nó phải phát triển. Đây có thể là một số nguyên lớn hơn 0 và nhỏ hơn: rehash-size (trong trường hợp này, nó sẽ được chia tỷ lệ bất cứ khi nào bảng được tăng lên) hoặc nó có thể là một số dấu phẩy động từ 0 đến 1. Giá trị mặc định cho giá trị này đối số phụ thuộc vào việc triển khai.

Bạn cũng có thể gọi hàm make-hash-table không có đối số.

Lấy các mục từ và thêm các mục vào bảng băm

Các gethashhàm lấy một mục từ bảng băm bằng cách tìm kiếm khóa của nó. Nếu nó không tìm thấy khóa, thì nó sẽ trả về nil.

Nó có cú pháp sau:

gethash key hash-table &optional default

ở đâu -

  • key: là khóa liên quan

  • bảng băm: là bảng băm được tìm kiếm

  • default: là giá trị được trả về, nếu không tìm thấy mục nhập, là nil, nếu không được chỉ định.

Các gethash hàm thực sự trả về hai giá trị, giá trị thứ hai là giá trị vị từ đúng nếu tìm thấy mục nhập và sai nếu không tìm thấy mục nhập.

Để thêm một mục vào bảng băm, bạn có thể sử dụng setf chức năng cùng với gethash chức năng.

Thí dụ

Tạo một tệp mã nguồn mới có tên main.lisp và nhập mã sau vào đó.

(setq empList (make-hash-table)) 
(setf (gethash '001 empList) '(Charlie Brown))
(setf (gethash '002 empList) '(Freddie Seal)) 
(write (gethash '001 empList)) 
(terpri)
(write (gethash '002 empList))

Khi bạn thực thi mã, nó trả về kết quả sau:

(CHARLIE BROWN)
(FREDDIE SEAL)

Xóa mục nhập

Các remhashhàm xóa bất kỳ mục nhập nào cho một khóa cụ thể trong bảng băm. Đây là một vị từ đúng nếu có mục nhập hoặc sai nếu không có.

Cú pháp cho hàm này là:

remhash key hash-table

Thí dụ

Tạo một tệp mã nguồn mới có tên main.lisp và nhập mã sau vào đó.

(setq empList (make-hash-table)) 
(setf (gethash '001 empList) '(Charlie Brown))
(setf (gethash '002 empList) '(Freddie Seal)) 
(setf (gethash '003 empList) '(Mark Mongoose)) 

(write (gethash '001 empList)) 
(terpri)
(write (gethash '002 empList)) 
(terpri)
(write (gethash '003 empList))  
(remhash '003 empList)
(terpri)
(write (gethash '003 empList))

Khi bạn thực thi mã, nó trả về kết quả sau:

(CHARLIE BROWN)
(FREDDIE SEAL)
(MARK MONGOOSE)
NIL

Hàm maphash

Các maphash hàm cho phép bạn áp dụng một hàm được chỉ định trên mỗi cặp khóa-giá trị trên bảng băm.

Nó cần hai đối số - hàm và một bảng băm và gọi hàm một lần cho mỗi cặp khóa / giá trị trong bảng băm.

Thí dụ

Tạo một tệp mã nguồn mới có tên main.lisp và nhập mã sau vào đó.

(setq empList (make-hash-table)) 
(setf (gethash '001 empList) '(Charlie Brown))
(setf (gethash '002 empList) '(Freddie Seal)) 
(setf (gethash '003 empList) '(Mark Mongoose)) 

(maphash #'(lambda (k v) (format t "~a => ~a~%" k v)) empList)

Khi bạn thực thi mã, nó trả về kết quả sau:

3 => (MARK MONGOOSE)
2 => (FREDDIE SEAL)
1 => (CHARLIE BROWN)

LISP chung cung cấp nhiều chức năng đầu vào-đầu ra. Chúng tôi đã sử dụng chức năng định dạng và chức năng in cho đầu ra. Trong phần này, chúng ta sẽ xem xét một số hàm đầu vào - đầu ra được sử dụng phổ biến nhất được cung cấp trong LISP.

Chức năng đầu vào

Bảng sau cung cấp các hàm đầu vào thường được sử dụng nhất của LISP:

Sr.No. Mô tả chức năng
1

read& tùy chọn input-stream eof-error-p eof-value recursive-p

Nó đọc trong biểu diễn in của một đối tượng Lisp từ luồng đầu vào, xây dựng một đối tượng Lisp tương ứng và trả về đối tượng.

2

read-preserving-whitespace& tùy chọn trong luồng eof-error-p eof-value đệ quy-p

Nó được sử dụng trong một số tình huống chuyên biệt, nơi muốn xác định chính xác ký tự nào đã kết thúc mã thông báo mở rộng.

3

read-line& tùy chọn input-stream eof-error-p eof-value recursive-p

Nó đọc trong một dòng văn bản được kết thúc bằng một dòng mới.

4

read-char& tùy chọn input-stream eof-error-p eof-value recursive-p

Nó nhận một ký tự từ luồng đầu vào và trả về nó như một đối tượng ký tự.

5

unread-char ký tự & luồng đầu vào tùy chọn

Nó đặt ký tự được đọc gần đây nhất từ ​​luồng đầu vào, lên phía trước luồng đầu vào.

6

peek-char& tùy chọn kiểu peek-type input-stream eof-error-p eof-value đệ quy-p

Nó trả về ký tự tiếp theo được đọc từ luồng đầu vào, mà không thực sự xóa nó khỏi luồng đầu vào.

7

listen& luồng đầu vào tùy chọn

Vị ngữ listen là true nếu có một ký tự ngay lập tức từ input-stream, và là false nếu không.

số 8

read-char-no-hang& tùy chọn input-stream eof-error-p eof-value recursive-p

Nó tương tự như read-char, nhưng nếu nó không nhận được một ký tự, nó không đợi một ký tự mà trả về nil ngay lập tức.

9

clear-input& luồng đầu vào tùy chọn

Nó xóa mọi đầu vào được đệm liên kết với luồng đầu vào.

10

read-from-string chuỗi & tùy chọn eof-error-p eof-value & key: start: end: keep-whitespace

Nó lấy các ký tự của chuỗi liên tiếp và xây dựng một đối tượng LISP và trả về đối tượng. Nó cũng trả về chỉ mục của ký tự đầu tiên trong chuỗi không được đọc hoặc độ dài của chuỗi (hoặc, độ dài +1), tùy từng trường hợp.

11

parse-integer string & key: start: end: radix: junk-allow

Nó kiểm tra chuỗi con của chuỗi được phân cách bởi: start và: end (mặc định là đầu và cuối của chuỗi). Nó bỏ qua các ký tự khoảng trắng và sau đó cố gắng phân tích cú pháp một số nguyên.

12

read-byte binary-input-stream & eof-error-p eof-value tùy chọn

Nó đọc một byte từ luồng nhị phân-đầu vào và trả về dưới dạng một số nguyên.

Đọc đầu vào từ bàn phím

Các readđược sử dụng để lấy đầu vào từ bàn phím. Nó có thể không có bất kỳ đối số.

Ví dụ: hãy xem xét đoạn mã -

(write ( + 15.0 (read)))

Giả sử người dùng nhập 10.2 từ Đầu vào STDIN, nó trả về,

25.2

Hàm read đọc các ký tự từ luồng đầu vào và giải thích chúng bằng cách phân tích cú pháp dưới dạng các đại diện của đối tượng Lisp.

Thí dụ

Tạo một tệp mã nguồn mới có tên main.lisp và nhập mã sau vào đó:

; the function AreaOfCircle
; calculates area of a circle
; when the radius is input from keyboard

(defun AreaOfCircle()
(terpri)
(princ "Enter Radius: ")
(setq radius (read))
(setq area (* 3.1416 radius radius))
(princ "Area: ")
(write area))
(AreaOfCircle)

Khi bạn thực thi mã, nó trả về kết quả sau:

Enter Radius: 5 (STDIN Input)
Area: 78.53999

Thí dụ

Tạo một tệp mã nguồn mới có tên main.lisp và nhập mã sau vào đó.

(with-input-from-string (stream "Welcome to Tutorials Point!")
   (print (read-char stream))
   (print (read-char stream))
   (print (read-char stream))
   (print (read-char stream))
   (print (read-char stream))
   (print (read-char stream))
   (print (read-char stream))
   (print (read-char stream))
   (print (read-char stream))
   (print (read-char stream))
   (print (peek-char nil stream nil 'the-end))
   (values)
)

Khi bạn thực thi mã, nó trả về kết quả sau:

#\W 
#\e 
#\l 
#\c 
#\o 
#\m 
#\e 
#\Space 
#\t 
#\o 
#\Space

Các chức năng đầu ra

Tất cả các hàm đầu ra trong LISP nhận một đối số tùy chọn được gọi là luồng đầu ra, nơi đầu ra được gửi. Nếu không được đề cập hoặc không, luồng đầu ra mặc định là giá trị của biến * tiêu chuẩn đầu ra *.

Bảng sau cung cấp các chức năng đầu ra thường được sử dụng nhất của LISP:

Sr.No. Chức năng và Mô tả
1

write object & key: stream: Escape: radix: base: circle: pretty: level: length: case: gensym: array

write object & key: stream: Escape: radix: base: circle: pretty: level: length: case: gensym: array: read Reading: right-margin: miser-width: lines: pprint-Dispatch

Cả hai đều ghi đối tượng vào luồng đầu ra được chỉ định bởi: stream, mặc định là giá trị của * standard-output *. Các giá trị khác được đặt mặc định cho các biến toàn cục tương ứng được đặt để in.

2

prin1 đối tượng & luồng đầu ra tùy chọn

print đối tượng & luồng đầu ra tùy chọn

pprint đối tượng & luồng đầu ra tùy chọn

princ đối tượng & luồng đầu ra tùy chọn

Tất cả các hàm này đưa ra biểu diễn in của đối tượng tới luồng xuất . Tuy nhiên, có những điểm khác biệt sau:

  • Prince1 trả về đối tượng dưới dạng giá trị của nó.

  • print in đối tượng với một dòng mới đứng trước và theo sau là một khoảng trắng. Nó trả về đối tượng.

  • pprint cũng giống như in ngoại trừ khoảng trống ở cuối bị bỏ qua.

  • princ giống như Prince1 ngoại trừ đầu ra không có ký tự thoát

3

write-to-string đối tượng & khóa : Escape: radix: base: circle: pretty: level: length: case: gensym: array

write-to-string đối tượng & khóa: thoát: cơ số: cơ sở: hình tròn: khá: cấp độ: chiều dài: trường hợp: gensym: mảng: dễ đọc: lề phải: chiều rộng sai: dòng: pprint-phái

prin1-to-string vật

princ-to-string vật

Đối tượng được in hiệu quả và các ký tự đầu ra được tạo thành một chuỗi, được trả về.

4

write-char ký tự & luồng đầu ra tùy chọn

Nó xuất ký tự ra luồng xuất và trả về ký tự.

5

write-string string & output-stream & key tùy chọn : start: end

Nó ghi các ký tự của chuỗi con được chỉ định của chuỗi vào luồng xuất.

6

write-line string & output-stream & key tùy chọn : start: end

Nó hoạt động theo cách tương tự như write-string, nhưng xuất ra một dòng mới sau đó.

7

terpri& luồng đầu ra tùy chọn

Nó xuất ra một dòng mới cho dòng xuất.

số 8

fresh-line& luồng đầu ra tùy chọn

nó chỉ xuất ra một dòng mới nếu dòng chưa ở đầu dòng.

9

finish-output& luồng đầu ra tùy chọn

force-output& luồng đầu ra tùy chọn

clear-output& luồng đầu ra tùy chọn

  • Chức năng finish-output cố gắng đảm bảo rằng tất cả đầu ra được gửi đến luồng đầu ra đã đến đích và chỉ sau đó trả về con số không.

  • Chức năng force-output bắt đầu làm trống bất kỳ bộ đệm nội bộ nào nhưng trả về nil mà không cần đợi hoàn thành hoặc xác nhận.

  • Chức năng clear-output cố gắng hủy bỏ bất kỳ hoạt động đầu ra nào đang diễn ra để cho phép đầu ra tiếp tục đến đích càng ít càng tốt.

10

write-byte số nguyên nhị phân-đầu ra-luồng

Nó ghi một byte, giá trị của số nguyên.

Thí dụ

Tạo một tệp mã nguồn mới có tên main.lisp và nhập mã sau vào đó.

; this program inputs a numbers and doubles it
(defun DoubleNumber()
   (terpri)
   (princ "Enter Number : ")
   (setq n1 (read))
   (setq doubled (* 2.0 n1))
   (princ "The Number: ")
   (write n1)
   (terpri)
   (princ "The Number Doubled: ")
   (write doubled)
)
(DoubleNumber)

Khi bạn thực thi mã, nó trả về kết quả sau:

Enter Number : 3456.78 (STDIN Input)
The Number: 3456.78
The Number Doubled: 6913.56

Đầu ra được định dạng

Chức năng formatđược sử dụng để tạo ra văn bản có định dạng độc đáo. Nó có cú pháp sau:

format destination control-string &rest arguments

Ở đâu,

  • đích là đầu ra tiêu chuẩn
  • chuỗi điều khiển giữ các ký tự được xuất và lệnh in.

A format directive bao gồm dấu ngã (~), các tham số tiền tố tùy chọn được phân tách bằng dấu phẩy, dấu hai chấm tùy chọn (:) và bổ ngữ dấu (@) và một ký tự duy nhất cho biết đây là loại lệnh nào.

Các tham số tiền tố thường là số nguyên, được ký hiệu là số thập phân có dấu tùy chọn.

Bảng sau cung cấp mô tả ngắn gọn về các lệnh thường được sử dụng:

Sr.No. Chỉ thị & Mô tả
1

~A

Theo sau là các đối số ASCII.

2

~S

Theo sau là biểu thức S.

3

~D

Đối với các đối số thập phân.

4

~B

Đối với các đối số nhị phân.

5

~O

Đối với các đối số bát phân.

6

~X

Đối với các đối số thập lục phân.

7

~C

Đối với các đối số ký tự.

số 8

~F

Đối với đối số dấu phẩy động định dạng cố định.

9

~E

Đối số dấu phẩy động hàm mũ.

10

~$

Đối số đô la và dấu phẩy động.

11

~%

Một dòng mới được in.

12

~*

Đối số tiếp theo bị bỏ qua.

13

~?

Sự chuyển hướng. Đối số tiếp theo phải là một chuỗi và đối số sau nó là một danh sách.

Thí dụ

Hãy để chúng tôi viết lại chương trình tính diện tích hình tròn -

Tạo một tệp mã nguồn mới có tên main.lisp và nhập mã sau vào đó.

(defun AreaOfCircle()
   (terpri)
   (princ "Enter Radius: ")
   (setq radius (read))
   (setq area (* 3.1416 radius radius))
   (format t "Radius: = ~F~% Area = ~F" radius area)
)
(AreaOfCircle)

Khi bạn thực thi mã, nó trả về kết quả sau:

Enter Radius: 10.234 (STDIN Input)
Radius: = 10.234
Area = 329.03473

Chúng tôi đã thảo luận về cách xử lý đầu vào và đầu ra tiêu chuẩn của LISP chung. Tất cả các chức năng này cũng hoạt động để đọc và ghi vào văn bản và tệp nhị phân. Chỉ khác là trong trường hợp này, luồng chúng ta sử dụng không phải là đầu vào hoặc đầu ra tiêu chuẩn, mà là luồng được tạo cho mục đích cụ thể là ghi vào hoặc đọc từ tệp.

Trong chương này, chúng ta sẽ xem cách LISP có thể tạo, mở, đóng các tệp văn bản hoặc tệp nhị phân để lưu trữ dữ liệu của chúng.

Tệp đại diện cho một chuỗi các byte, không quan trọng đó là tệp văn bản hay tệp nhị phân. Chương này sẽ hướng dẫn bạn qua các hàm / macro quan trọng để quản lý tệp.

Mở tệp

Bạn có thể dùng openchức năng tạo tệp mới hoặc mở tệp hiện có. Đây là chức năng cơ bản nhất để mở tệp. Tuy nhiên,with-open-file thường thuận tiện hơn và được sử dụng phổ biến hơn, như chúng ta sẽ thấy ở phần sau của phần này.

Khi một tệp được mở, một đối tượng luồng được xây dựng để đại diện cho nó trong môi trường LISP. Tất cả các thao tác trên luồng về cơ bản tương đương với các thao tác trên tệp.

Cú pháp cho open chức năng là -

open filename &key :direction :element-type :if-exists :if-does-not-exist :external-format

Ở đâu,

  • Đối số tên tệp là tên của tệp sẽ được mở hoặc tạo.

  • Các đối số từ khóa chỉ định loại luồng và các cách xử lý lỗi.

  • Các :direction từ khóa chỉ định liệu luồng sẽ xử lý đầu vào, đầu ra hay cả hai, nó nhận các giá trị sau:

    • : input - cho các luồng đầu vào (giá trị mặc định)

    • : output - cho các luồng đầu ra

    • : io - cho các luồng hai chiều

    • : probe - để chỉ kiểm tra sự tồn tại của tệp; dòng được mở và sau đó đóng lại.

  • Các :element-type chỉ định loại đơn vị giao dịch cho luồng.

  • Các :if-existsđối số chỉ định hành động được thực hiện nếu: hướng là: đầu ra hoặc: io và tệp có tên được chỉ định đã tồn tại. Nếu hướng là: input hoặc: probe, đối số này bị bỏ qua. Nó có các giá trị sau:

    • : error - nó báo hiệu một lỗi.

    • : new-version - nó tạo một tệp mới có cùng tên nhưng số phiên bản lớn hơn.

    • : rename - nó đổi tên tệp hiện có.

    • : rename-and-delete - nó đổi tên tệp hiện có và sau đó xóa nó.

    • : append - nó gắn vào tệp hiện có.

    • : supersede - nó thay thế tệp hiện có.

    • nil - nó không tạo một tệp hoặc thậm chí một luồng chỉ trả về nil để chỉ ra sự thất bại.

  • Các :if-does-not-existđối số chỉ định hành động sẽ được thực hiện nếu tệp có tên được chỉ định chưa tồn tại. Nó có các giá trị sau:

    • : error - nó báo hiệu một lỗi.

    • : create - nó tạo một tệp trống với tên được chỉ định và sau đó sử dụng nó.

    • nil - nó không tạo một tệp hoặc thậm chí một luồng, mà thay vào đó chỉ trả về nil để chỉ ra sự thất bại.

  • Các :external-format đối số chỉ định một lược đồ được triển khai công nhận để biểu diễn các ký tự trong tệp.

Ví dụ: bạn có thể mở một tệp có tên myfile.txt được lưu trữ trong thư mục / tmp dưới dạng -

(open "/tmp/myfile.txt")

Ghi và đọc từ tệp

Các with-open-filecho phép đọc hoặc ghi vào một tệp, sử dụng biến luồng được liên kết với giao dịch đọc / ghi. Sau khi hoàn thành công việc, nó sẽ tự động đóng tệp. Nó là cực kỳ thuận tiện để sử dụng.

Nó có cú pháp sau:

with-open-file (stream filename {options}*)
   {declaration}* {form}*
  • tên tệp là tên của tệp sẽ được mở; nó có thể là một chuỗi, một tên đường dẫn hoặc một luồng.

  • Các tùy chọn giống như các đối số từ khóa cho hàm đang mở.

ví dụ 1

Tạo một tệp mã nguồn mới có tên main.lisp và nhập mã sau vào đó.

(with-open-file (stream "/tmp/myfile.txt" :direction :output)
   (format stream "Welcome to Tutorials Point!")
   (terpri stream)
   (format stream "This is a tutorials database")
   (terpri stream)
   (format stream "Submit your Tutorials, White Papers and Articles into our Tutorials   Directory.")
)

Xin lưu ý rằng tất cả các hàm đầu vào-đầu ra được thảo luận trong chương trước, chẳng hạn như, terpri và định dạng đang hoạt động để ghi vào tệp chúng tôi đã tạo ở đây.

Khi bạn thực thi mã, nó không trả về bất cứ thứ gì; tuy nhiên, dữ liệu của chúng tôi được ghi vào tệp. Các:direction :output từ khóa cho phép chúng tôi làm điều này.

Tuy nhiên, chúng tôi có thể đọc từ tệp này bằng cách sử dụng read-line chức năng.

Ví dụ 2

Tạo một tệp mã nguồn mới có tên main.lisp và nhập mã sau vào đó.

(let ((in (open "/tmp/myfile.txt" :if-does-not-exist nil)))
   (when in
      (loop for line = (read-line in nil)
      
      while line do (format t "~a~%" line))
      (close in)
   )
)

Khi bạn thực thi mã, nó trả về kết quả sau:

Welcome to Tutorials Point!
This is a tutorials database
Submit your Tutorials, White Papers and Articles into our Tutorials Directory.

Đóng tệp

Các close hàm đóng một luồng.

Cấu trúc là một trong những kiểu dữ liệu do người dùng xác định, cho phép bạn kết hợp các mục dữ liệu thuộc các loại khác nhau.

Các cấu trúc được sử dụng để biểu diễn một bản ghi. Giả sử bạn muốn theo dõi sách của mình trong thư viện. Bạn có thể muốn theo dõi các thuộc tính sau về mỗi cuốn sách -

  • Title
  • Author
  • Subject
  • Mã sách

Xác định cấu trúc

Các defstructmacro trong LISP cho phép bạn xác định cấu trúc bản ghi trừu tượng. Cácdefstruct câu lệnh xác định một kiểu dữ liệu mới, với nhiều hơn một thành viên cho chương trình của bạn.

Để thảo luận về định dạng của defstructmacro, hãy để chúng tôi viết định nghĩa của cấu trúc Sách. Chúng ta có thể xác định cấu trúc cuốn sách là -

(defstruct book 
   title 
   author 
   subject 
   book-id 
)

Xin lưu ý

  • Khai báo trên tạo một cấu trúc sách với bốn named components. Vì vậy, mọi cuốn sách được tạo ra sẽ là một đối tượng của cấu trúc này.

  • Nó xác định bốn hàm có tên book-title, book-author, book-subject và book-book-id, sẽ lấy một đối số, cấu trúc sách và sẽ trả về các trường tên sách, tác giả, chủ đề và id sách của cuốn sách vật. Các chức năng này được gọi làaccess functions.

  • Sổ ký hiệu trở thành một kiểu dữ liệu và bạn có thể kiểm tra nó bằng cách sử dụng typep Thuộc tính.

  • Cũng sẽ có một hàm ngầm có tên book-p, là một vị từ và sẽ đúng nếu đối số của nó là một cuốn sách và sai nếu ngược lại.

  • Một hàm ẩn khác có tên make-book sẽ được tạo ra, đó là một constructor, mà khi được gọi, sẽ tạo ra một cấu trúc dữ liệu với bốn thành phần, thích hợp để sử dụng với các chức năng truy cập.

  • Các #S syntax đề cập đến một cấu trúc và bạn có thể sử dụng nó để đọc hoặc in các bản sao của một cuốn sách.

  • Một hàm ngầm định có tên là copy-book của một đối số cũng được định nghĩa như vậy. Nó lấy một đối tượng sách và tạo một đối tượng sách khác, là bản sao của đối tượng đầu tiên. Chức năng này được gọi làcopier function.

  • Bạn có thể dùng setf để thay đổi các thành phần của một cuốn sách, chẳng hạn như

(setf (book-book-id book3) 100)

Thí dụ

Tạo một tệp mã nguồn mới có tên main.lisp và nhập mã sau vào đó.

(defstruct book 
   title 
   author 
   subject 
   book-id 
)

( setq book1 (make-book :title "C Programming"
   :author "Nuha Ali" 
   :subject "C-Programming Tutorial"
   :book-id "478")
)

( setq book2 (make-book :title "Telecom Billing"
   :author "Zara Ali" 
   :subject "C-Programming Tutorial"
   :book-id "501")
) 

(write book1)
(terpri)
(write book2)
(setq book3( copy-book book1))
(setf (book-book-id book3) 100) 
(terpri)
(write book3)

Khi bạn thực thi mã, nó trả về kết quả sau:

#S(BOOK :TITLE "C Programming" :AUTHOR "Nuha Ali" :SUBJECT "C-Programming Tutorial" :BOOK-ID "478")
#S(BOOK :TITLE "Telecom Billing" :AUTHOR "Zara Ali" :SUBJECT "C-Programming Tutorial" :BOOK-ID "501")
#S(BOOK :TITLE "C Programming" :AUTHOR "Nuha Ali" :SUBJECT "C-Programming Tutorial" :BOOK-ID 100)

Theo thuật ngữ chung của các ngôn ngữ lập trình, một gói được thiết kế để cung cấp cách giữ một bộ tên riêng biệt với một bộ tên khác. Các ký hiệu được khai báo trong một gói sẽ không xung đột với các ký hiệu tương tự được khai báo trong gói khác. Bằng cách này các gói làm giảm xung đột đặt tên giữa các mô-đun mã độc lập.

Trình đọc LISP duy trì một bảng gồm tất cả các ký hiệu mà nó đã tìm thấy. Khi tìm thấy một chuỗi ký tự mới, nó sẽ tạo một ký hiệu mới và lưu trữ trong bảng ký hiệu. Bảng này được gọi là một gói.

Gói hiện tại được tham chiếu bởi biến đặc biệt * gói *.

Có hai gói được xác định trước trong LISP -

  • common-lisp - nó chứa các ký hiệu cho tất cả các hàm và biến được định nghĩa.

  • common-lisp-user- nó sử dụng gói common-lisp và tất cả các gói khác với các công cụ chỉnh sửa và gỡ lỗi; nó được gọi ngắn gọn là cl-user

Gói chức năng trong LISP

Bảng sau cung cấp các hàm thường được sử dụng nhất được sử dụng để tạo, sử dụng và thao tác các gói:

Sr.No. Chức năng và Mô tả
1

make-package package-name & key: nicknames: use

Nó tạo và trả về một gói mới với tên gói được chỉ định.

2

in-package package-name & key: nicknames: use

Làm cho gói hiện tại.

3

in-package Tên

Macro này khiến * gói * được đặt thành tên gói có tên, phải là một ký hiệu hoặc chuỗi.

4

find-package Tên

Nó tìm kiếm một gói. Gói có tên hoặc biệt hiệu đó được trả lại; nếu không có gói nào như vậy tồn tại, gói tìm kiếm trả về nil.

5

rename-package gói tên mới & biệt hiệu mới tùy chọn

nó đổi tên một gói.

6

list-all-packages

Hàm này trả về danh sách tất cả các gói hiện đang tồn tại trong hệ thống Lisp.

7

delete-package gói hàng

Nó xóa một gói.

Tạo một gói LISP

Các defpackageđược sử dụng để tạo một gói do người dùng xác định. Nó có cú pháp sau:

(defpackage :package-name
   (:use :common-lisp ...)
   (:export :symbol1 :symbol2 ...)
)

Ở đâu,

  • package-name là tên của gói.

  • Từ khóa: use chỉ định các gói mà gói này cần, tức là các gói xác định các chức năng được sử dụng bởi mã trong gói này.

  • Từ khóa: export chỉ định các ký hiệu bên ngoài trong gói này.

Các make-packagecũng được sử dụng để tạo một gói. Cú pháp cho hàm này là:

make-package package-name &key :nicknames :use

các đối số và từ khóa có cùng ý nghĩa như trước.

Sử dụng một gói

Khi bạn đã tạo một gói, bạn có thể sử dụng mã trong gói này, bằng cách đặt nó thành gói hiện tại. Cácin-package macro làm cho một gói hiện hành trong môi trường.

Thí dụ

Tạo một tệp mã nguồn mới có tên main.lisp và nhập mã sau vào đó.

(make-package :tom)
(make-package :dick)
(make-package :harry)
(in-package tom)
(defun hello () 
   (write-line "Hello! This is Tom's Tutorials Point")
)

(hello)
(in-package dick)
(defun hello () 
   (write-line "Hello! This is Dick's Tutorials Point")
)

(hello)
(in-package harry)
(defun hello () 
   (write-line "Hello! This is Harry's Tutorials Point")
)

(hello)
(in-package tom)
(hello)
(in-package dick)
(hello)
(in-package harry)
(hello)

Khi bạn thực thi mã, nó trả về kết quả sau:

Hello! This is Tom's Tutorials Point
Hello! This is Dick's Tutorials Point
Hello! This is Harry's Tutorials Point

Xóa một gói

Các delete-packagemacro cho phép bạn xóa một gói. Ví dụ sau đây chứng minh điều này -

Thí dụ

Tạo một tệp mã nguồn mới có tên main.lisp và nhập mã sau vào đó.

(make-package :tom)
(make-package :dick)
(make-package :harry)
(in-package tom)
(defun hello () 
   (write-line "Hello! This is Tom's Tutorials Point")
)

(in-package dick)
(defun hello () 
   (write-line "Hello! This is Dick's Tutorials Point")
)

(in-package harry)
(defun hello () 
   (write-line "Hello! This is Harry's Tutorials Point")
)

(in-package tom)
(hello)
(in-package dick)
(hello)
(in-package harry)
(hello)
(delete-package tom)
(in-package tom)
(hello)

Khi bạn thực thi mã, nó trả về kết quả sau:

Hello! This is Tom's Tutorials Point
Hello! This is Dick's Tutorials Point
Hello! This is Harry's Tutorials Point
*** - EVAL: variable TOM has no value

Trong thuật ngữ LISP thông thường, các ngoại lệ được gọi là điều kiện.

Trên thực tế, các điều kiện tổng quát hơn các ngoại lệ trong các ngôn ngữ lập trình truyền thống, bởi vì condition đại diện cho bất kỳ sự xuất hiện, lỗi hoặc không, có thể ảnh hưởng đến các cấp độ khác nhau của ngăn xếp lệnh gọi hàm.

Cơ chế xử lý điều kiện trong LISP, xử lý các tình huống như vậy theo cách mà các điều kiện được sử dụng để phát tín hiệu cảnh báo (giả sử bằng cách in cảnh báo) trong khi mã cấp trên trên ngăn xếp cuộc gọi có thể tiếp tục công việc của nó.

Hệ thống xử lý điều kiện trong LISP có ba phần:

  • Báo hiệu một điều kiện
  • Xử lý tình trạng
  • Khởi động lại quá trình

Xử lý một điều kiện

Chúng ta hãy lấy một ví dụ về việc xử lý một điều kiện phát sinh từ điều kiện chia cho 0, để giải thích các khái niệm ở đây.

Bạn cần thực hiện các bước sau để xử lý một điều kiện:

  • Define the Condition - "Một điều kiện là một đối tượng mà lớp của nó chỉ ra bản chất chung của điều kiện và dữ liệu thể hiện của nó mang thông tin về chi tiết của các trường hợp cụ thể dẫn đến điều kiện được báo hiệu".

    Macro xác định điều kiện được sử dụng để xác định một điều kiện, có cú pháp sau:

    (define-condition condition-name (error)
       ((text :initarg :text :reader text))
    )

    Các đối tượng điều kiện mới được tạo bằng macro MAKE-CONDITION, macro này sẽ khởi tạo các vị trí của điều kiện mới dựa trên :initargs argument.

    In our example, the following code defines the condition −

    (define-condition on-division-by-zero (error)
       ((message :initarg :message :reader message))
    )
  • Writing the Handlers − a condition handler is a code that are used for handling the condition signalled thereon. It is generally written in one of the higher level functions that call the erroring function. When a condition is signalled, the signalling mechanism searches for an appropriate handler based on the condition's class.

    Each handler consists of −

    • Type specifier, that indicates the type of condition it can handle
    • A function that takes a single argument, the condition

    When a condition is signalled, the signalling mechanism finds the most recently established handler that is compatible with the condition type and calls its function.

    The macro handler-case establishes a condition handler. The basic form of a handler-case −

    (handler-case expression error-clause*)

    Where, each error clause is of the form −

    condition-type ([var]) code)
  • Restarting Phase

    This is the code that actually recovers your program from errors, and condition handlers can then handle a condition by invoking an appropriate restart. The restart code is generally place in middle-level or low-level functions and the condition handlers are placed into the upper levels of the application.

    The handler-bind macro allows you to provide a restart function, and allows you to continue at the lower level functions without unwinding the function call stack. In other words, the flow of control will still be in the lower level function.

    The basic form of handler-bind is as follows −

    (handler-bind (binding*) form*)

    Where each binding is a list of the following −

    • a condition type
    • a handler function of one argument

    The invoke-restart macro finds and invokes the most recently bound restart function with the specified name as argument.

    You can have multiple restarts.

Example

In this example, we demonstrate the above concepts by writing a function named division-function, which will create an error condition if the divisor argument is zero. We have three anonymous functions that provide three ways to come out of it - by returning a value 1, by sending a divisor 2 and recalculating, or by returning 1.

Create a new source code file named main.lisp and type the following code in it.

(define-condition on-division-by-zero (error)
   ((message :initarg :message :reader message))
)
   
(defun handle-infinity ()
   (restart-case
      (let ((result 0))
         (setf result (division-function 10 0))
         (format t "Value: ~a~%" result)
      )
      (just-continue () nil)
   )
)
     
(defun division-function (value1 value2)
   (restart-case
      (if (/= value2 0)
         (/ value1 value2)
         (error 'on-division-by-zero :message "denominator is zero")
      )

      (return-zero () 0)
      (return-value (r) r)
      (recalc-using (d) (division-function value1 d))
   )
)

(defun high-level-code ()
   (handler-bind
      (
         (on-division-by-zero
            #'(lambda (c)
               (format t "error signaled: ~a~%" (message c))
               (invoke-restart 'return-zero)
            )
         )
         (handle-infinity)
      )
   )
)

(handler-bind
   (
      (on-division-by-zero
         #'(lambda (c)
            (format t "error signaled: ~a~%" (message c))
            (invoke-restart 'return-value 1)
         )
      )
   )
   (handle-infinity)
)

(handler-bind
   (
      (on-division-by-zero
         #'(lambda (c)
            (format t "error signaled: ~a~%" (message c))
            (invoke-restart 'recalc-using 2)
         )
      )
   )
   (handle-infinity)
)

(handler-bind
   (
      (on-division-by-zero
         #'(lambda (c)
            (format t "error signaled: ~a~%" (message c))
            (invoke-restart 'just-continue)
         )
      )
   )
   (handle-infinity)
)

(format t "Done."))

When you execute the code, it returns the following result −

error signaled: denominator is zero
Value: 1
error signaled: denominator is zero
Value: 5
error signaled: denominator is zero
Done.

Apart from the 'Condition System', as discussed above, Common LISP also provides various functions that may be called for signalling an error. Handling of an error, when signalled, is however, implementation-dependent.

Error Signalling Functions in LISP

The following table provides commonly used functions signalling warnings, breaks, non-fatal and fatal errors.

The user program specifies an error message (a string). The functions process this message and may/may not display it to the user.

The error messages should be constructed by applying the format function, should not contain a newline character at either the beginning or end, and need not indicate error, as the LISP system will take care of these according to its preferred style.

Sr.No. Function and Description
1

error format-string &rest args

It signals a fatal error. It is impossible to continue from this kind of error; thus error will never return to its caller.

2

cerror continue-format-string error-format-string &rest args

It signals an error and enters the debugger. However, it allows the program to be continued from the debugger after resolving the error.

3

warn format-string &rest args

it prints an error message but normally doesn't go into the debugger

4

break &optional format-string &rest args

It prints the message and goes directly into the debugger, without allowing any possibility of interception by programmed error-handling facilities

Example

In this example, the factorial function calculates factorial of a number; however, if the argument is negative, it raises an error condition.

Create a new source code file named main.lisp and type the following code in it.

(defun factorial (x)
   (cond ((or (not (typep x 'integer)) (minusp x))
      (error "~S is a negative number." x))
      ((zerop x) 1)
      (t (* x (factorial (- x 1))))
   )
)

(write(factorial 5))
(terpri)
(write(factorial -1))

When you execute the code, it returns the following result −

120
*** - -1 is a negative number.

Common LISP predated the advance of object-oriented programming by couple of decades. However, it object-orientation was incorporated into it at a later stage.

Defining Classes

The defclass macro allows creating user-defined classes. It establishes a class as a data type. It has the following syntax −

(defclass class-name (superclass-name*)
   (slot-description*)
   class-option*))

The slots are variables that store data, or fields.

A slot-description has the form (slot-name slot-option*), where each option is a keyword followed by a name, expression and other options. Most commonly used slot options are −

  • :accessor function-name

  • :initform expression

  • :initarg symbol

For example, let us define a Box class, with three slots length, breadth, and height.

(defclass Box () 
   (length 
   breadth 
   height)
)

Providing Access and Read/Write Control to a Slot

Unless the slots have values that can be accessed, read or written to, classes are pretty useless.

You can specify accessors for each slot when you define a class. For example, take our Box class −

(defclass Box ()
   ((length :accessor length)
      (breadth :accessor breadth)
      (height :accessor height)
   )
)

You can also specify separate accessor names for reading and writing a slot.

(defclass Box ()
   ((length :reader get-length :writer set-length)
      (breadth :reader get-breadth :writer set-breadth)
      (height :reader get-height :writer set-height)
   )
)

Creating Instance of a Class

The generic function make-instance creates and returns a new instance of a class.

It has the following syntax −

(make-instance class {initarg value}*)

Example

Let us create a Box class, with three slots, length, breadth and height. We will use three slot accessors to set the values in these fields.

Create a new source code file named main.lisp and type the following code in it.

(defclass box ()
   ((length :accessor box-length)
      (breadth :accessor box-breadth)
      (height :accessor box-height)
   )
)
(setf item (make-instance 'box))
(setf (box-length item) 10)
(setf (box-breadth item) 10)
(setf (box-height item) 5)
(format t "Length of the Box is ~d~%" (box-length item))
(format t "Breadth of the Box is ~d~%" (box-breadth item))
(format t "Height of the Box is ~d~%" (box-height item))

Khi bạn thực thi mã, nó trả về kết quả sau:

Length of the Box is 10
Breadth of the Box is 10
Height of the Box is 5

Định nghĩa một phương pháp lớp

Các defmethodmacro cho phép bạn xác định một phương thức bên trong lớp. Ví dụ sau mở rộng lớp Box của chúng ta để bao gồm một phương thức có tên là volume.

Tạo một tệp mã nguồn mới có tên main.lisp và nhập mã sau vào đó.

(defclass box ()
   ((length :accessor box-length)
      (breadth :accessor box-breadth)
      (height :accessor box-height)
      (volume :reader volume)
   )
)

; method calculating volume   

(defmethod volume ((object box))
   (* (box-length object) (box-breadth object)(box-height object))
)

 ;setting the values 

(setf item (make-instance 'box))
(setf (box-length item) 10)
(setf (box-breadth item) 10)
(setf (box-height item) 5)

; displaying values

(format t "Length of the Box is ~d~%" (box-length item))
(format t "Breadth of the Box is ~d~%" (box-breadth item))
(format t "Height of the Box is ~d~%" (box-height item))
(format t "Volume of the Box is ~d~%" (volume item))

Khi bạn thực thi mã, nó trả về kết quả sau:

Length of the Box is 10
Breadth of the Box is 10
Height of the Box is 5
Volume of the Box is 500

Di sản

LISP cho phép bạn xác định một đối tượng dưới dạng đối tượng khác. Đây được gọi làinheritance.Bạn có thể tạo một lớp dẫn xuất bằng cách thêm các tính năng mới hoặc khác. Lớp dẫn xuất kế thừa các chức năng của lớp cha.

Ví dụ sau giải thích điều này -

Thí dụ

Tạo một tệp mã nguồn mới có tên main.lisp và nhập mã sau vào đó.

(defclass box ()
   ((length :accessor box-length)
      (breadth :accessor box-breadth)
      (height :accessor box-height)
      (volume :reader volume)
   )
)

; method calculating volume   
(defmethod volume ((object box))
   (* (box-length object) (box-breadth object)(box-height object))
)
  
;wooden-box class inherits the box class  
(defclass wooden-box (box)
((price :accessor box-price)))

;setting the values 
(setf item (make-instance 'wooden-box))
(setf (box-length item) 10)
(setf (box-breadth item) 10)
(setf (box-height item) 5)
(setf (box-price item) 1000)

; displaying values
(format t "Length of the Wooden Box is ~d~%" (box-length item))
(format t "Breadth of the Wooden Box is ~d~%" (box-breadth item))
(format t "Height of the Wooden Box is ~d~%" (box-height item))
(format t "Volume of the Wooden Box is ~d~%" (volume item))
(format t "Price of the Wooden Box is ~d~%" (box-price item))

Khi bạn thực thi mã, nó trả về kết quả sau:

Length of the Wooden Box is 10
Breadth of the Wooden Box is 10
Height of the Wooden Box is 5
Volume of the Wooden Box is 500
Price of the Wooden Box is 1000