LISP - Szybki przewodnik
John McCarthy wynalazł LISP w 1958 roku, krótko po opracowaniu FORTRAN-u. Po raz pierwszy został wdrożony przez Steve'a Russella na komputerze IBM 704.
Szczególnie nadaje się do programów wykorzystujących sztuczną inteligencję, ponieważ skutecznie przetwarza symboliczne informacje.
Common Lisp powstał w latach osiemdziesiątych i dziewięćdziesiątych XX wieku w celu ujednolicenia pracy kilku grup wdrożeniowych, które były następcami Maclisp, takich jak ZetaLisp i NIL (New Implementation of Lisp) itp.
Służy jako wspólny język, który można łatwo rozszerzyć do konkretnej implementacji.
Programy napisane w Common LISP nie zależą od cech specyficznych dla maszyny, takich jak długość słowa itp.
Funkcje Common LISP
Jest niezależny od maszyny
Wykorzystuje iteracyjną metodologię projektowania i łatwą rozszerzalność.
Umożliwia dynamiczną aktualizację programów.
Zapewnia debugowanie wysokiego poziomu.
Zapewnia zaawansowane programowanie obiektowe.
Zapewnia wygodny system makr.
Zapewnia szeroki zakres typów danych, takich jak obiekty, struktury, listy, wektory, regulowane tablice, tablice skrótów i symbole.
Opiera się na wyrażeniach.
Zapewnia zorientowany obiektowo system warunków.
Zapewnia pełną bibliotekę I / O.
Zapewnia rozbudowane struktury sterowania.
Aplikacje wbudowane w LISP
Duże udane aplikacje zbudowane w Lisp.
Emacs
G2
AutoCad
Igor Engraver
Sklep Yahoo
Konfiguracja środowiska lokalnego
Jeśli nadal chcesz skonfigurować swoje środowisko dla języka programowania Lisp, potrzebujesz następujących dwóch programów dostępnych na swoim komputerze: (a) Edytor tekstu i (b) Lisp Executer.
Edytor tekstu
Będzie to użyte do wpisania twojego programu. Przykłady kilku edytorów obejmują Notatnik Windows, polecenie edycji systemu operacyjnego, Brief, Epsilon, EMACS i vim lub vi.
Nazwa i wersja edytora tekstu mogą się różnić w różnych systemach operacyjnych. Na przykład Notatnik będzie używany w systemie Windows, a vim lub vi może być używany w systemie Windows, a także w systemie Linux lub UNIX.
Pliki utworzone za pomocą edytora nazywane są plikami źródłowymi i zawierają kod źródłowy programu. Pliki źródłowe programów Lisp są zwykle nazywane z rozszerzeniem ".lisp”.
Przed rozpoczęciem programowania upewnij się, że masz jeden edytor tekstu i masz wystarczające doświadczenie, aby napisać program komputerowy, zapisać go w pliku, a na koniec wykonać.
Lisp Executer
Kod źródłowy zapisany w pliku źródłowym jest czytelnym dla człowieka źródłem programu. Musi zostać „wykonany”, aby przekształcić się w język maszynowy, aby procesor mógł faktycznie wykonać program zgodnie z podanymi instrukcjami.
Ten język programowania Lisp zostanie użyty do wykonania kodu źródłowego do końcowego programu wykonywalnego. Zakładam, że masz podstawową wiedzę o języku programowania.
CLISP to multi-architekturalny kompilator GNU Common LISP używany do konfigurowania LISP w systemie Windows. Wersja dla Windows emuluje środowisko unixowe używając MingW pod oknami. Zajmuje się tym instalator i automatycznie dodaje clisp do zmiennej PATH systemu Windows.
Możesz pobrać najnowszą wersję CLISP dla Windows tutaj - https://sourceforge.net/projects/clisp/files/latest/download
Domyślnie tworzy skrót w menu Start dla interpretera linia po linii.
Jak korzystać z CLISP
Podczas instalacji, clisp jest automatycznie dodawany do zmiennej PATH, jeśli wybierzesz opcję (ZALECANE). Oznacza to, że możesz po prostu otworzyć nowe okno wiersza polecenia i wpisać „clisp”, aby wywołać kompilator.
Aby uruchomić plik * .lisp lub * .lsp, po prostu użyj -
clisp hello.lisp
Wyrażenia LISP są nazywane wyrażeniami symbolicznymi lub s-wyrażeniami. Wyrażenia S składają się z trzech ważnych obiektów, atomów, list i łańcuchów.
Każde wyrażenie s jest poprawnym programem.
Programy LISP działają na platformie interpreter lub jako compiled code.
Interpreter sprawdza kod źródłowy w powtarzanej pętli, która jest również nazywana pętlą odczytu-oceny-wydruku (REPL). Odczytuje kod programu, ocenia go i wyświetla wartości zwrócone przez program.
Prosty program
Napiszmy wyrażenie s, aby znaleźć sumę trzech liczb 7, 9 i 11. Aby to zrobić, możemy wpisać po znaku zachęty interpretera.
(+ 7 9 11)
LISP zwraca wynik -
27
Jeśli chcesz uruchomić ten sam program, co skompilowany kod, utwórz plik kodu źródłowego LISP o nazwie myprog.lisp i wpisz w nim następujący kod.
(write (+ 7 9 11))
Kiedy klikniesz przycisk Wykonaj lub naciśniesz Ctrl + E, LISP wykona go natychmiast, a zwrócony wynik to -
27
LISP używa notacji prefiksowej
Być może zauważyłeś, że LISP używa prefix notation.
W powyższym programie symbol + działa jako nazwa funkcji dla procesu sumowania liczb.
W notacji przedrostków operatory są zapisywane przed ich operandami. Na przykład wyrażenie
a * ( b + c ) / d
zostanie zapisane jako -
(/ (* a (+ b c) ) d)
Weźmy inny przykład, napiszmy kod konwertujący temperaturę Fahrenheita 60 o F na skalę Celsjusza -
Matematycznym wyrażeniem tej konwersji będzie -
(60 * 9 / 5) + 32
Utwórz plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
(write(+ (* (/ 9 5) 60) 32))
Kiedy klikniesz przycisk Wykonaj lub naciśniesz Ctrl + E, LISP wykona go natychmiast i zwrócony wynik to -
140
Ocena programów LISP
Ocena programów LISP składa się z dwóch części -
Tłumaczenie tekstu programu na obiekty Lispa przez program czytający
Implementacja semantyki języka w zakresie tych obiektów przez program ewaluacyjny
Proces oceny obejmuje następujące kroki -
Czytelnik tłumaczy ciągi znaków na obiekty LISP lub s-expressions.
Ewaluator definiuje składnię Lisp formsktóre są zbudowane z s-wyrażeń. Ten drugi poziom oceny definiuje składnię, która określa, któres-expressions to formularze LISP.
Ewaluator działa jako funkcja, która przyjmuje prawidłową formę LISP jako argument i zwraca wartość. To jest powód, dla którego umieściliśmy wyrażenie LISP w nawiasach, ponieważ wysyłamy całe wyrażenie / formularz do oceniającego jako argumenty.
Program „Hello World”
Nauka nowego języka programowania tak naprawdę nie rozpocznie się, dopóki nie nauczysz się witać całego świata w tym języku, prawda!
Dlatego utwórz nowy plik z kodem źródłowym o nazwie main.lisp i wpisz w nim następujący kod.
(write-line "Hello World")
(write-line "I am at 'Tutorials Point'! Learning LISP")
Kiedy klikniesz przycisk Wykonaj lub naciśniesz Ctrl + E, LISP wykona go natychmiast, a zwrócony wynik to -
Hello World
I am at 'Tutorials Point'! Learning LISP
Podstawowe bloki konstrukcyjne w LISP
Programy LISP składają się z trzech podstawowych bloków konstrukcyjnych -
- atom
- list
- string
Na atomto liczba lub ciąg ciągłych znaków. Zawiera cyfry i znaki specjalne.
Poniżej znajdują się przykłady niektórych prawidłowych atomów -
hello-from-tutorials-point
name
123008907
*hello*
Block#221
abc123
ZA list jest sekwencją atomów i / lub innych list umieszczonych w nawiasach.
Poniżej znajdują się przykłady niektórych prawidłowych list -
( i am a list)
(a ( a b c) d e fgh)
(father tom ( susan bill joe))
(sun mon tue wed thur fri sat)
( )
ZA string to grupa znaków ujęta w podwójne cudzysłowy.
Poniżej znajdują się przykłady niektórych prawidłowych ciągów -
" I am a string"
"a ba c d efg #$%^&!"
"Please enter the following details :"
"Hello from 'Tutorials Point'! "
Dodawanie komentarzy
Symbol średnika (;) służy do wskazania wiersza komentarza.
Na przykład,
(write-line "Hello World") ; greet the world
; tell them your whereabouts
(write-line "I am at 'Tutorials Point'! Learning LISP")
Kiedy klikniesz przycisk Wykonaj lub naciśniesz Ctrl + E, LISP wykona go natychmiast, a zwrócony wynik to -
Hello World
I am at 'Tutorials Point'! Learning LISP
Kilka ważnych punktów przed przejściem do następnego
Poniżej przedstawiono kilka ważnych punktów, na które należy zwrócić uwagę -
Podstawowe operacje numeryczne w LISP to +, -, * i /
LISP reprezentuje wywołanie funkcji f (x) jako (fx), na przykład cos (45) jest zapisywane jako cos 45
W wyrażeniach LISP jest rozróżniana wielkość liter, cos 45 lub COS 45 są takie same.
LISP próbuje ocenić wszystko, łącznie z argumentami funkcji. Tylko trzy typy elementów są stałymi i zawsze zwracają własną wartość
Numbers
Litera t, to oznacza logiczną prawdę.
Wartość nil, co oznacza logiczne fałsz, a także pustą listę.
Niewiele więcej o formularzach LISP
W poprzednim rozdziale wspomnieliśmy, że proces oceny kodu LISP obejmuje następujące kroki.
Czytelnik tłumaczy ciągi znaków na obiekty LISP lub s-expressions.
Ewaluator definiuje składnię Lisp formsktóre są zbudowane z s-wyrażeń. Ten drugi poziom oceny definiuje składnię, która określa, które wyrażenia s są formami LISP.
Teraz mogą być formularze LISP-a.
- Atom
- Pusta lub nie będąca listą
- Dowolna lista, której pierwszym elementem jest symbol
Ewaluator działa jako funkcja, która przyjmuje prawidłową formę LISP jako argument i zwraca wartość. To jest powód, dla którego umieściliśmyLISP expression in parenthesis, ponieważ wysyłamy całe wyrażenie / formularz do oceniającego jako argumenty.
Konwencje nazewnictwa w LISP
Nazwa lub symbole mogą składać się z dowolnej liczby znaków alfanumerycznych innych niż białe znaki, nawiasy otwierające i zamykające, cudzysłowy podwójne i pojedyncze, ukośnik odwrotny, przecinek, dwukropek, średnik i kreska pionowa. Aby użyć tych znaków w nazwie, musisz użyć znaku zmiany znaczenia (\).
Nazwa może mieć cyfry, ale nie w całości składać się z cyfr, ponieważ wtedy byłaby odczytywana jako liczba. Podobnie nazwa może mieć kropki, ale nie może składać się wyłącznie z kropek.
Stosowanie pojedynczego cudzysłowu
LISP ocenia wszystko, łącznie z argumentami funkcji i członkami listy.
Czasami musimy brać atomy lub listy dosłownie i nie chcemy, aby były oceniane lub traktowane jako wywołania funkcji.
Aby to zrobić, musimy poprzedzić atom lub listę pojedynczym cudzysłowem.
Poniższy przykład ilustruje to.
Utwórz plik o nazwie main.lisp i wpisz w nim następujący kod.
(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))
Kiedy klikniesz przycisk Wykonaj lub naciśniesz Ctrl + E, LISP wykona go natychmiast, a zwrócony wynik to -
single quote used, it inhibits evaluation
(* 2 3)
single quote not used, so expression evaluated
6
W LISP zmienne nie są wpisywane, ale obiekty danych.
Typy danych LISP można podzielić na.
Scalar types - na przykład rodzaje liczb, znaki, symbole itp.
Data structures - na przykład listy, wektory, wektory bitowe i łańcuchy.
Każda zmienna może przyjąć dowolny obiekt LISP jako swoją wartość, chyba że zadeklarowałeś to jawnie.
Chociaż nie jest konieczne określanie typu danych dla zmiennej LISP, pomaga to jednak w niektórych rozszerzeniach pętli, w deklaracjach metod i innych sytuacjach, które omówimy w dalszych rozdziałach.
Typy danych są ułożone w hierarchię. Typ danych to zbiór obiektów LISP i wiele obiektów może należeć do jednego takiego zestawu.
Plik typep predykat służy do znajdowania, czy obiekt należy do określonego typu.
Plik type-of funkcja zwraca typ danych danego obiektu.
Specyfikatory typu w LISP
Specyfikatory typu to symbole zdefiniowane przez system dla typów danych.
szyk | fixnum | pakiet | prosty ciąg |
atom | pływak | nazwa ścieżki | prosty wektor |
bignum | funkcjonować | stan losowy | pojedynczy zmiennoprzecinkowy |
kawałek | tabela skrótów | stosunek | standard-char |
wektor bitowy | liczba całkowita | racjonalny | strumień |
postać | słowo kluczowe | czytelny | strunowy |
[wspólny] | lista | sekwencja | [string-char] |
funkcja skompilowana | długi pływak | short-float | symbol |
złożony | nill | bajt ze znakiem | t |
Cons | zero | prosta tablica | bajt bez znaku |
double-float | numer | prosty wektor bitowy | wektor |
Oprócz tych typów zdefiniowanych w systemie można tworzyć własne typy danych. Gdy typ konstrukcji jest zdefiniowany za pomocądefstruct funkcja, nazwa typu konstrukcji staje się poprawnym symbolem typu.
Przykład 1
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
(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)
Kiedy klikniesz przycisk Wykonaj lub naciśniesz Ctrl + E, LISP wykona go natychmiast, a zwrócony wynik to -
10
34.567
123.78
NIL
110000.0
62
Przykład 2
Następnie sprawdźmy typy zmiennych użytych w poprzednim przykładzie. Utwórz nowy plik kodu źródłowego o nazwie main. lisp i wpisz w nim następujący kod.
(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))
Kiedy klikniesz przycisk Wykonaj lub naciśniesz Ctrl + E, LISP wykona go natychmiast, a zwrócony wynik to -
(INTEGER 0 281474976710655)
SINGLE-FLOAT
SINGLE-FLOAT
NULL
SINGLE-FLOAT
(INTEGER 0 281474976710655)
Makra pozwalają na rozszerzenie składni standardowego LISP-a.
Z technicznego punktu widzenia makro to funkcja, która przyjmuje jako argumenty wyrażenie s i zwraca formularz LISP, który jest następnie oceniany.
Definiowanie makra
W LISP, nazwane makro jest definiowane przy użyciu innego makra o nazwie defmacro. Składnia do definiowania makra to -
(defmacro macro-name (parameter-list))
"Optional documentation string."
body-form
Definicja makra składa się z nazwy makra, listy parametrów, opcjonalnego ciągu dokumentacji oraz treści wyrażeń Lisp, które definiują zadanie, które ma być wykonane przez makro.
Przykład
Napiszmy proste makro o nazwie setTo10, które przyjmie liczbę i ustawi jej wartość na 10.
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
(defmacro setTo10(num)
(setq num 10)(print num))
(setq x 25)
(print x)
(setTo10 x)
Kiedy klikniesz przycisk Wykonaj lub naciśniesz Ctrl + E, LISP wykona go natychmiast, a zwrócony wynik to -
25
10
W LISP każda zmienna jest reprezentowana przez symbol. Nazwa zmiennej jest nazwą symbolu i jest przechowywana w komórce pamięci symbolu.
Zmienne globalne
Zmienne globalne mają stałe wartości w całym systemie LISP i pozostają w mocy do momentu określenia nowej wartości.
Zmienne globalne są zwykle deklarowane przy użyciu rozszerzenia defvar zbudować.
Na przykład
(defvar x 234)
(write x)
Kiedy klikniesz przycisk Wykonaj lub naciśniesz Ctrl + E, LISP wykona go natychmiast, a zwrócony wynik to
234
Ponieważ w LISP nie ma deklaracji typu dla zmiennych, bezpośrednio określasz wartość symbolu z rozszerzeniem setq zbudować.
Na przykład
->(setq x 10)
Powyższe wyrażenie przypisuje wartość 10 do zmiennej x. Możesz odwołać się do zmiennej używając samego symbolu jako wyrażenia.
Plik symbol-value Funkcja umożliwia wydobycie wartości przechowywanej w miejscu przechowywania symbolu.
Na przykład
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
(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)
Kiedy klikniesz przycisk Wykonaj lub naciśniesz Ctrl + E, LISP wykona go natychmiast, a zwrócony wynik to.
x = 10 y = 20
x = 100 y = 200
Zmienne lokalne
Zmienne lokalne są definiowane w ramach danej procedury. Parametry nazwane jako argumenty w definicji funkcji są również zmiennymi lokalnymi. Zmienne lokalne są dostępne tylko w ramach odpowiedniej funkcji.
Podobnie jak zmienne globalne, zmienne lokalne można również tworzyć przy użyciu rozszerzenia setq zbudować.
Istnieją dwie inne konstrukcje - let i prog do tworzenia zmiennych lokalnych.
Konstrukcja let ma następującą składnię.
(let ((var1 val1) (var2 val2).. (varn valn))<s-expressions>)
Gdzie var1, var2, ..varn to nazwy zmiennych, a val1, val2, .. valn to początkowe wartości przypisane do odpowiednich zmiennych.
Gdy letjest wykonywana, każdej zmiennej przypisywana jest odpowiednia wartość, a na koniec oceniane jest wyrażenie s . Zwracana jest wartość ostatniego ocenianego wyrażenia.
Jeśli nie podasz wartości początkowej zmiennej, zostanie ona przypisana nil.
Przykład
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
(let ((x 'a) (y 'b)(z 'c))
(format t "x = ~a y = ~a z = ~a" x y z))
Kiedy klikniesz przycisk Wykonaj lub naciśniesz Ctrl + E, LISP wykona go natychmiast, a zwrócony wynik to.
x = A y = B z = C
Plik prog Konstrukcja ma również listę zmiennych lokalnych jako pierwszy argument, po którym następuje treść prog, i dowolną liczbę wyrażeń s.
Plik prog function wykonuje listę wyrażeń s w sekwencji i zwraca nil, chyba że napotka wywołanie funkcji o nazwie return. Następnie argument return funkcja jest obliczana i zwracana.
Przykład
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
(prog ((x '(a b c))(y '(1 2 3))(z '(p q 10)))
(format t "x = ~a y = ~a z = ~a" x y z))
Kiedy klikniesz przycisk Wykonaj lub naciśniesz Ctrl + E, LISP wykona go natychmiast, a zwrócony wynik to.
x = (A B C) y = (1 2 3) z = (P Q 10)
W LISP-ie stałe są zmiennymi, które nigdy nie zmieniają swoich wartości podczas wykonywania programu. Stałe są deklarowane przy użyciudefconstant zbudować.
Przykład
Poniższy przykład pokazuje deklarowanie globalnej stałej PI, a później użycie tej wartości wewnątrz funkcji o nazwie area-circle, która oblicza pole koła.
Plik defun konstrukcja służy do definiowania funkcji, przyjrzymy się jej w Functions rozdział.
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
(defconstant PI 3.141592)
(defun area-circle(rad)
(terpri)
(format t "Radius: ~5f" rad)
(format t "~%Area: ~10f" (* PI rad rad)))
(area-circle 10)
Kiedy klikniesz przycisk Wykonaj lub naciśniesz Ctrl + E, LISP wykona go natychmiast, a zwrócony wynik to.
Radius: 10.0
Area: 314.1592
Operator to symbol, który mówi kompilatorowi, aby wykonał określone operacje matematyczne lub logiczne. LISP umożliwia liczne operacje na danych, obsługiwane przez różne funkcje, makra i inne konstrukcje.
Operacje dozwolone na danych można sklasyfikować jako -
- Działania arytmetyczne
- Operacje porównawcze
- Operacje logiczne
- Operacje bitowe
Działania arytmetyczne
Poniższa tabela przedstawia wszystkie operatory arytmetyczne obsługiwane przez LISP. Przyjmij zmiennąA posiada 10 i zmienną B mieści wtedy 20 -
Show Examples
Operator | Opis | Przykład |
---|---|---|
+ | Dodaje dwa operandy | (+ AB) da 30 |
- | Odejmuje drugi operand od pierwszego | (- AB) da -10 |
* | Mnoży oba operandy | (* AB) daje 200 |
/ | Dzieli licznik przez de-licznik | (/ BA) da 2 |
mod, rem | Operator modułu i reszta po dzieleniu całkowitoliczbowym | (mod BA) da 0 |
incf | Operator inkrementacji zwiększa wartość całkowitą o drugi podany argument | (w tym A 3) daje 13 |
decf | Operator zmniejszenia zmniejsza wartość całkowitą o drugi podany argument | (decf A 4) da 9 |
Operacje porównawcze
Poniższa tabela przedstawia wszystkie operatory relacyjne obsługiwane przez LISP, które porównują liczby. Jednak w przeciwieństwie do operatorów relacyjnych w innych językach, operatory porównania LISP mogą przyjmować więcej niż dwa operandy i działają tylko na liczbach.
Przyjmij zmienną A posiada 10 i zmienną B mieści 20, a następnie -
Show Examples
Operator | Opis | Przykład |
---|---|---|
= | Sprawdza, czy wszystkie wartości operandów są równe, czy nie. Jeśli tak, warunek staje się prawdziwy. | (= AB) nie jest prawdą. |
/ = | Sprawdza, czy wartości operandów są różne, czy nie. Jeśli wartości nie są równe, warunek staje się prawdziwy. | (/ = AB) jest prawdą. |
> | Sprawdza, czy wartości operandów maleją monotonicznie. | (> AB) nie jest prawdą. |
< | Sprawdza, czy wartości operandów rosną monotonicznie. | (<AB) jest prawdą. |
> = | Sprawdza, czy wartość dowolnego lewego operandu jest większa lub równa wartości następnego prawego operandu, jeśli tak, warunek staje się prawdziwy. | (> = AB) nie jest prawdą. |
<= | Sprawdza, czy wartość dowolnego lewego operandu jest mniejsza lub równa wartości jego prawego operandu. Jeśli tak, warunek staje się prawdziwy. | (<= AB) jest prawdą. |
max | Porównuje dwa lub więcej argumentów i zwraca maksymalną wartość. | (max AB) zwraca 20 |
min | Porównuje dwa lub więcej argumentów i zwraca wartość minimalną. | (min AB) zwraca 10 |
Operacje logiczne na wartościach boolowskich
Wspólny LISP udostępnia trzy operatory logiczne: and, or, i notktóry działa na wartościach logicznych. ZałożyćA ma wartość nil i B ma wartość 5, to -
Show Examples
Operator | Opis | Przykład |
---|---|---|
i | Wymaga dowolnej liczby argumentów. Argumenty są oceniane od lewej do prawej. Jeśli wartości wszystkich argumentów są różne od zera, zwracana jest wartość ostatniego argumentu. W przeciwnym razie zwracane jest nil. | (i AB) zwróci NIL. |
lub | Wymaga dowolnej liczby argumentów. Argumenty są obliczane od lewej do prawej, dopóki jeden nie uzyska wartości różnej od zera, w takim przypadku zwracana jest wartość argumentu, w przeciwnym razie zwracanil. | (lub AB) zwróci wartość 5. |
nie | Pobiera jeden argument i zwraca t jeśli argument ma wartość nil. | (nie A) zwróci T. |
Operacje bitowe na liczbach
Operatory bitowe pracują na bitach i wykonują operacje bit po bicie. Tabele prawdy dla operacji bitowych i, or, i xor są następujące -
Show Examples
p | q | p i q | p lub q | p xlub 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
W poniższej tabeli wymieniono operatory bitowe obsługiwane przez LISP. Przyjmij zmiennąA mieści 60 i zmienną B posiada 13, a następnie -
Operator | Opis | Przykład |
---|---|---|
logand | Zwraca bitowe logiczne I argumentów. Jeśli nie podano żadnego argumentu, wynikiem jest -1, co jest tożsamością tej operacji. | (logand ab)) da 12 |
logior | Zwraca bitową logiczną wartość INCLUSIVE OR jej argumentów. Jeśli nie podano argumentu, wynikiem jest zero, co stanowi tożsamość tej operacji. | (logior ab) da 61 |
logxor | Zwraca bitową logiczną WYŁĄCZNĄ LUB jej argumentów. Jeśli nie podano argumentu, wynikiem jest zero, co stanowi tożsamość tej operacji. | (logxor ab) da 49 |
lognor | Zwraca bitową wartość NIE swoich argumentów. Jeśli nie podano żadnego argumentu, wynikiem jest -1, co jest tożsamością tej operacji. | (lognor ab) da -62, |
logeqv | Zwraca bitową logiczną RÓWNOWAŻNOŚĆ (nazywaną również wyłączną nor) swoich argumentów. Jeśli nie podano żadnego argumentu, wynikiem jest -1, co jest tożsamością tej operacji. | (logeqv ab) da -50 |
Struktury decyzyjne wymagają, aby programista określił jeden lub więcej warunków, które mają być ocenione lub przetestowane przez program, wraz z instrukcją lub instrukcjami, które mają zostać wykonane, jeśli warunek zostanie określony jako prawdziwy, i opcjonalnie inne instrukcje do wykonania, jeśli warunek jest zdeterminowany, aby być fałszywy.
Poniżej przedstawiono ogólną formę typowej struktury podejmowania decyzji występującej w większości języków programowania -
LISP zapewnia następujące typy konstrukcji decyzyjnych. Kliknij poniższe łącza, aby sprawdzić ich szczegóły.
Sr.No. | Konstrukcja i opis |
---|---|
1 | dyr Ta konstrukcja jest używana do sprawdzania wielu klauzul test-action. Można go porównać do zagnieżdżonych instrukcji if w innych językach programowania. |
2 | gdyby Konstrukcja if ma różne formy. W najprostszej formie następuje po nim klauzula testowa, akcja testowa i kilka innych działań następczych. Jeśli klauzula test ma wartość true, wówczas akcja testowa jest wykonywana w przeciwnym razie, oceniana jest klauzula konsekwentna. |
3 | gdy W najprostszej formie następuje klauzula testowa i akcja testowa. Jeśli klauzula test ma wartość true, wówczas akcja testowa jest wykonywana w przeciwnym razie, oceniana jest klauzula konsekwentna. |
4 | walizka Ta konstrukcja implementuje wiele klauzul test-action, takich jak konstrukcja cond. Jednak ocenia kluczową formę i zezwala na wiele klauzul działania w oparciu o ocenę tego klucza. |
Może zaistnieć sytuacja, w której trzeba będzie wykonać kilka razy blok kodu. Instrukcja pętli umożliwia wielokrotne wykonanie instrukcji lub grupy instrukcji, a następująca po niej jest ogólną postacią instrukcji pętli w większości języków programowania.
LISP udostępnia następujące typy konstrukcji do obsługi wymagań dotyczących pętli. Kliknij poniższe łącza, aby sprawdzić ich szczegóły.
Sr.No. | Konstrukcja i opis |
---|---|
1 | pętla Plik loopkonstrukcja jest najprostszą formą iteracji udostępnianą przez LISP. W najprostszej formie pozwala na wielokrotne wykonywanie niektórych instrukcji, aż znajdzie plikreturn komunikat. |
2 | pętla dla Pętla for umożliwia implementację iteracji podobnej do pętli for, jak najczęściej w innych językach. |
3 | zrobić Konstrukcja do jest również używana do wykonywania iteracji za pomocą LISP. Zapewnia ustrukturyzowaną formę iteracji. |
4 | dotimes Konstrukcja dotimes umożliwia zapętlenie określonej liczby iteracji. |
5 | zrobić listę Konstrukcja dolist umożliwia iterację przez każdy element listy. |
Wdzięczne wyjście z bloku
Plik block i return-from pozwala na łagodne wyjście z zagnieżdżonych bloków w przypadku jakiegokolwiek błędu.
Plik blockFunkcja umożliwia utworzenie nazwanego bloku z treścią składającą się z zera lub większej liczby instrukcji. Składnia to -
(block block-name(
...
...
))
Plik return-from funkcja przyjmuje nazwę bloku i opcjonalną (domyślnie zero) wartość zwracaną.
Poniższy przykład demonstruje to -
Przykład
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod -
(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)
Kiedy klikniesz przycisk Wykonaj lub naciśniesz Ctrl + E, LISP wykona go natychmiast, a zwrócony wynik to -
ENTERING-OUTER-BLOCK
ENTERING-INNER-BLOCK
ENTERING-OUTER-BLOCK
ENTERING-INNER-BLOCK
5
LEFT-INNER-BLOCK
LEAVING-OUTER-BLOCK
Funkcja to grupa instrukcji, które razem wykonują zadanie.
Możesz podzielić swój kod na osobne funkcje. To, w jaki sposób podzielisz swój kod na różne funkcje, zależy od Ciebie, ale logicznie jest to zazwyczaj taki podział, że każda funkcja wykonuje określone zadanie.
Definiowanie funkcji w LISP
Makro o nazwie defunsłuży do definiowania funkcji. Plikdefun makro potrzebuje trzech argumentów -
- Nazwa funkcji
- Parametry funkcji
- Treść funkcji
Składnia defun to -
(defun name (parameter-list) "Optional documentation string." body)
Zilustrujmy to pojęcie prostymi przykładami.
Przykład 1
Napiszmy funkcję o nazwie averagenum , która wypisze średnią z czterech liczb. Wyślemy te liczby jako parametry.
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
(defun averagenum (n1 n2 n3 n4)
(/ ( + n1 n2 n3 n4) 4)
)
(write(averagenum 10 20 30 40))
Po wykonaniu kodu zwraca następujący wynik -
25
Przykład 2
Zdefiniujmy i wywołajmy funkcję, która obliczy pole koła, gdy promień okręgu zostanie podany jako argument.
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
(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)
Po wykonaniu kodu zwraca następujący wynik -
Radius: 10.0
Area: 314.1592
Należy pamiętać, że -
Możesz podać pustą listę jako parametry, co oznacza, że funkcja nie przyjmuje argumentów, lista jest pusta, zapisana jako ().
LISP dopuszcza również argumenty opcjonalne, wielokrotne i słowa kluczowe.
Ciąg dokumentacji opisuje cel funkcji. Jest powiązany z nazwą funkcji i można go uzyskać za pomocą plikudocumentation funkcjonować.
Ciało funkcji może składać się z dowolnej liczby wyrażeń Lispa.
Wartość ostatniego wyrażenia w treści jest zwracana jako wartość funkcji.
Możesz również zwrócić wartość z funkcji za pomocą return-from operator specjalny.
Omówmy pokrótce powyższe pojęcia. Kliknij poniższe linki, aby znaleźć szczegółowe informacje -
Parametry opcjonalne
Parametry odpoczynku
Parametry słów kluczowych
Zwracanie wartości z funkcji
Funkcje lambda
Funkcje mapowania
Predykaty to funkcje, które sprawdzają swoje argumenty pod kątem określonych warunków i zwracają zero, jeśli warunek jest fałszywy lub jeśli warunek jest prawdziwy, lub jakaś wartość inna niż zero to prawda.
W poniższej tabeli przedstawiono niektóre z najczęściej używanych predykatów -
Sr.No. | Predykat i opis |
---|---|
1 | atom Pobiera jeden argument i zwraca t, jeśli argumentem jest atom lub nil, jeśli jest inaczej. |
2 | equal Pobiera dwa argumenty i zwraca t jeśli są strukturalnie równe lub nil Inaczej. |
3 | eq Pobiera dwa argumenty i zwraca t jeśli są to te same identyczne obiekty, współdzielące tę samą lokalizację pamięci lub nil Inaczej. |
4 | eql Pobiera dwa argumenty i zwraca t jeśli argumentami są eqlub jeśli są to liczby tego samego typu o tej samej wartości lub jeśli są to obiekty znakowe, które reprezentują ten sam znak, lub nil Inaczej. |
5 | evenp Pobiera jeden argument numeryczny i zwraca t jeśli argument jest liczbą parzystą lub nil jeśli inaczej. |
6 | oddp Pobiera jeden argument numeryczny i zwraca t jeśli argument jest liczbą nieparzystą lub nil jeśli inaczej. |
7 | zerop Pobiera jeden argument numeryczny i zwraca t jeśli argumentem jest zero lub nil jeśli inaczej. |
8 | null Pobiera jeden argument i zwraca t jeśli wartość argumentu wynosi zero, w przeciwnym razie zwraca nil. |
9 | listp Pobiera jeden argument i zwraca t jeśli argument zwraca listę, w przeciwnym razie zwraca nil. |
10 | greaterp Pobiera jeden lub więcej argumentów i zwraca t jeśli jest pojedynczy argument lub argumenty są kolejno większe od lewej do prawej, lub nil jeśli inaczej. |
11 | lessp Pobiera jeden lub więcej argumentów i zwraca t jeśli jest pojedynczy argument lub argumenty są sukcesywnie mniejsze od lewej do prawej, lub nil jeśli inaczej. |
12 | numberp Pobiera jeden argument i zwraca t jeśli argument jest liczbą lub nil jeśli inaczej. |
13 | symbolp Pobiera jeden argument i zwraca t jeśli argument jest symbolem, w przeciwnym razie zwraca nil. |
14 | integerp Pobiera jeden argument i zwraca t jeśli argument jest liczbą całkowitą, w przeciwnym razie zwraca nil. |
15 | rationalp Pobiera jeden argument i zwraca t jeśli argument jest liczbą wymierną, albo stosunkiem, albo liczbą, w przeciwnym razie zwraca nil. |
16 | floatp Pobiera jeden argument i zwraca t jeśli argument jest liczbą zmiennoprzecinkową, w przeciwnym razie zwraca nil. |
17 | realp Pobiera jeden argument i zwraca t jeśli argument jest liczbą rzeczywistą, w przeciwnym razie zwraca nil. |
18 | complexp Pobiera jeden argument i zwraca t jeśli argument jest liczbą zespoloną, w przeciwnym razie zwraca nil. |
19 | characterp Pobiera jeden argument i zwraca t jeśli argument jest znakiem, w przeciwnym razie zwraca nil. |
20 | stringp Pobiera jeden argument i zwraca t jeśli argument jest obiektem typu string, w przeciwnym razie zwraca nil. |
21 | arrayp Pobiera jeden argument i zwraca t jeśli argument jest obiektem tablicy, w przeciwnym razie zwraca nil. |
22 | packagep Pobiera jeden argument i zwraca t jeśli argument jest pakietem, w przeciwnym razie zwraca nil. |
Przykład 1
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
(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 ))
Po wykonaniu kodu zwraca następujący wynik -
T
NIL
T
NIL
T
NIL
NIL
NIL
T
Przykład 2
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
(defun factorial (num)
(cond ((zerop num) 1)
(t ( * num (factorial (- num 1))))
)
)
(setq n 6)
(format t "~% Factorial ~d is: ~d" n (factorial n))
Po wykonaniu kodu zwraca następujący wynik -
Factorial 6 is: 720
Common Lisp definiuje kilka rodzajów liczb. Pliknumber typ danych zawiera różne rodzaje liczb obsługiwane przez LISP.
Typy liczb obsługiwane przez LISP to -
- Integers
- Ratios
- Liczb zmiennoprzecinkowych
- Liczby zespolone
Poniższy diagram przedstawia hierarchię numerów i różne numeryczne typy danych dostępne w LISP -
Różne typy liczbowe w LISP
W poniższej tabeli opisano różne typy liczb dostępne w LISP -
Sr.No. | Typ danych i opis |
---|---|
1 | fixnum Ten typ danych reprezentuje liczby całkowite, które nie są zbyt duże i przeważnie mieszczą się w zakresie od -215 do 215-1 (zależy od maszyny) |
2 | bignum Są to bardzo duże liczby, których rozmiar jest ograniczony ilością pamięci przydzielonej dla LISP, nie są to liczby stałe. |
3 | ratio Reprezentuje stosunek dwóch liczb w postaci licznika / mianownika. Funkcja / zawsze daje wynik w stosunkach, gdy jej argumenty są liczbami całkowitymi. |
4 | float Reprezentuje liczby niecałkowite. Istnieją cztery typy danych zmiennoprzecinkowych o rosnącej precyzji. |
5 | complex Reprezentuje liczby zespolone, które są oznaczone przez #c. Części rzeczywiste i urojone mogą być liczbami wymiernymi lub zmiennoprzecinkowymi. |
Przykład
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
(write (/ 1 2))
(terpri)
(write ( + (/ 1 2) (/ 3 4)))
(terpri)
(write ( + #c( 1 2) #c( 3 -4)))
Po wykonaniu kodu zwraca następujący wynik -
1/2
5/4
#C(4 -2)
Funkcje liczbowe
W poniższej tabeli opisano niektóre często używane funkcje numeryczne -
Sr.No. | Opis funkcji |
---|---|
1 | +, -, *, / Odpowiednie operacje arytmetyczne |
2 | sin, cos, tan, acos, asin, atan Odpowiednie funkcje trygonometryczne. |
3 | sinh, cosh, tanh, acosh, asinh, atanh Odpowiednie funkcje hiperboliczne. |
4 | exp Funkcja potęgowania. Oblicza e x |
5 | expt Funkcja potęgowania przyjmuje zarówno podstawę, jak i moc. |
6 | sqrt Oblicza pierwiastek kwadratowy z liczby. |
7 | log Funkcja logarytmiczna. Jeśli podany jest jeden parametr, oblicza on swój logarytm naturalny, w przeciwnym razie drugi parametr jest używany jako podstawa. |
8 | conjugate Oblicza sprzężony koniugat liczby. W przypadku liczby rzeczywistej zwraca samą liczbę. |
9 | abs Zwraca wartość bezwzględną (lub wielkość) liczby. |
10 | gcd Oblicza największy wspólny dzielnik podanych liczb. |
11 | lcm Oblicza najmniejszą wspólną wielokrotność podanych liczb. |
12 | isqrt Daje największą liczbę całkowitą mniejszą lub równą dokładnemu pierwiastkowi kwadratowemu z danej liczby naturalnej. |
13 | floor, ceiling, truncate, round Wszystkie te funkcje przyjmują dwa argumenty jako liczbę i zwracają iloraz; floor zwraca największą liczbę całkowitą, która nie jest większa niż współczynnik, ceiling wybiera mniejszą liczbę całkowitą, która jest większa niż współczynnik, truncate wybiera liczbę całkowitą o tym samym znaku co stosunek o największej wartości bezwzględnej, która jest mniejsza niż wartość bezwzględna stosunku, i round wybiera liczbę całkowitą, która jest najbliższa stosunkowi. |
14 | ffloor, fceiling, ftruncate, fround Robi to samo, co powyżej, ale zwraca iloraz jako liczbę zmiennoprzecinkową. |
15 | mod, rem Zwraca resztę z operacji dzielenia. |
16 | float Konwertuje liczbę rzeczywistą na liczbę zmiennoprzecinkową. |
17 | rational, rationalize Konwertuje liczbę rzeczywistą na liczbę wymierną. |
18 | numerator, denominator Zwraca odpowiednie części liczby wymiernej. |
19 | realpart, imagpart Zwraca część rzeczywistą i urojoną liczby zespolonej. |
Przykład
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
(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))
Po wykonaniu kodu zwraca następujący wynik -
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
W LISP znaki są reprezentowane jako obiekty danych typu character.
Możesz oznaczyć obiekt znakowy poprzedzający # \ przed samym znakiem. Na przykład # \ a oznacza znak a.
Spację i inne znaki specjalne można oznaczyć poprzedzającym # \ przed nazwą znaku. Na przykład # \ SPACJA reprezentuje znak spacji.
Poniższy przykład demonstruje to -
Przykład
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
(write 'a)
(terpri)
(write #\a)
(terpri)
(write-char #\a)
(terpri)
(write-char 'a)
Po wykonaniu kodu zwraca następujący wynik -
A
#\a
a
*** - WRITE-CHAR: argument A is not a character
Znaki specjalne
Common LISP pozwala na używanie w kodzie następujących znaków specjalnych. Nazywa się je postaciami półstandardowymi.
- #\Backspace
- #\Tab
- #\Linefeed
- #\Page
- #\Return
- #\Rubout
Funkcje porównywania znaków
Numeryczne funkcje i operatory porównania, takie jak <i>, nie działają na znakach. Common LISP udostępnia dwa inne zestawy funkcji do porównywania znaków w kodzie.
W jednym zestawie rozróżniana jest wielkość liter, a w drugim wielkość liter.
Poniższa tabela przedstawia funkcje -
Funkcje uwzględniające wielkość liter | Funkcje bez rozróżniania wielkości liter | Opis |
---|---|---|
char = | znak równości | Sprawdza, czy wszystkie wartości operandów są równe, czy nie. Jeśli tak, warunek staje się prawdziwy. |
char / = | char-not-equal | Sprawdza, czy wartości operandów są różne, czy nie. Jeśli wartości nie są równe, warunek staje się prawdziwy. |
char < | char-lessp | Sprawdza, czy wartości operandów maleją monotonicznie. |
char> | char-Greaterp | Sprawdza, czy wartości operandów rosną monotonicznie. |
char <= | char-not-Greaterp | Sprawdza, czy wartość dowolnego lewego operandu jest większa lub równa wartości następnego prawego operandu, jeśli tak, warunek staje się prawdziwy. |
char> = | char-not-lessp | Sprawdza, czy wartość dowolnego lewego operandu jest mniejsza lub równa wartości jego prawego operandu. Jeśli tak, warunek staje się prawdziwy. |
Przykład
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
; 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))
Po wykonaniu kodu zwraca następujący wynik -
NIL
T
NIL
T
NIL
T
NIL
LISP umożliwia definiowanie tablic jedno- lub wielowymiarowych przy użyciu rozszerzenia make-arrayfunkcjonować. Tablica może przechowywać dowolny obiekt LISP jako swoje elementy.
Wszystkie tablice składają się z ciągłych lokalizacji pamięci. Najniższy adres odpowiada pierwszemu elementowi, a najwyższy adres ostatniemu elementowi.
Liczba wymiarów tablicy nazywana jest jej rangą.
W LISP element tablicy jest określany przez sekwencję nieujemnych liczb całkowitych. Długość sekwencji musi być równa pozycji tablicy. Indeksowanie zaczyna się od zera.
Na przykład, aby utworzyć tablicę z 10 komórkami o nazwie moja-tablica, możemy napisać -
(setf my-array (make-array '(10)))
Funkcja aref umożliwia dostęp do zawartości komórek. Pobiera dwa argumenty, nazwę tablicy i wartość indeksu.
Na przykład, aby uzyskać dostęp do zawartości dziesiątej komórki, piszemy -
(aref my-array 9)
Przykład 1
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
(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)
Po wykonaniu kodu zwraca następujący wynik -
#(NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL)
#(25 23 45 10 20 17 25 19 67 30)
Przykład 2
Stwórzmy tablicę 3 na 3.
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
(setf x (make-array '(3 3)
:initial-contents '((0 1 2 ) (3 4 5) (6 7 8)))
)
(write x)
Po wykonaniu kodu zwraca następujący wynik -
#2A((0 1 2) (3 4 5) (6 7 8))
Przykład 3
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
(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))
)
)
Po wykonaniu kodu zwraca następujący wynik -
(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)
Pełna składnia funkcji make-array
Funkcja make-array przyjmuje wiele innych argumentów. Spójrzmy na pełną składnię tej funkcji -
make-array dimensions :element-type :initial-element :initial-contents :adjustable :fill-pointer :displaced-to :displaced-index-offset
Oprócz argumentu wymiarów wszystkie inne argumenty są słowami kluczowymi. Poniższa tabela zawiera krótki opis argumentów.
Sr.No. | Argument i opis |
---|---|
1 | dimensions Podaje wymiary tablicy. Jest to liczba dla tablicy jednowymiarowej i lista dla tablicy wielowymiarowej. |
2 | :element-type Jest to specyfikator typu, wartość domyślna to T, czyli dowolny typ |
3 | :initial-element Wartość elementów początkowych. Spowoduje to utworzenie tablicy ze wszystkimi elementami zainicjalizowanymi do określonej wartości. |
4 | :initial-content Treść początkowa jako obiekt. |
5 | :adjustable Pomaga w tworzeniu wektora o zmiennym rozmiarze (lub regulowanym), którego rozmiar pamięci bazowej można zmienić. Argument jest wartością logiczną wskazującą, czy tablica jest regulowana, czy nie, przy czym wartość domyślna to NIL. |
6 | :fill-pointer Śledzi liczbę elementów faktycznie przechowywanych w wektorze o zmiennym rozmiarze. |
7 | :displaced-to Pomaga w tworzeniu przesuniętej tablicy lub tablicy współdzielonej, która dzieli swoją zawartość z określoną tablicą. Obie tablice powinny mieć ten sam typ elementu. Opcji: displaced-to nie można używać z opcją: initial-element lub: initial-content. Ten argument ma wartość domyślną nil. |
8 | :displaced-index-offset Daje przesunięcie indeksu utworzonej tablicy współdzielonej. |
Przykład 4
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
(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)
Po wykonaniu kodu zwraca następujący wynik -
#3A(((A B C) (1 2 3)) ((D E F) (4 5 6)) ((G H I) (7 8 9)))
#(C 1 2 3)
Jeśli przemieszczona tablica jest dwuwymiarowa -
(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)
Po wykonaniu kodu zwraca następujący wynik -
#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))
Zmieńmy przesunięcie przesuniętego wskaźnika na 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)
Po wykonaniu kodu zwraca następujący wynik -
#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))
Przykład 5
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
;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)
Po wykonaniu kodu zwraca następujący wynik -
#(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))
Łańcuchy w Common Lisp to wektory, tj. Jednowymiarowa tablica znaków.
Literały łańcuchowe są ujęte w podwójne cudzysłowy. Każdy znak obsługiwany przez zestaw znaków może zostać ujęty w cudzysłów, aby utworzyć ciąg, z wyjątkiem znaku podwójnego cudzysłowu (") i znaku zmiany znaczenia (\). Można je jednak uwzględnić, poprzedzając je ukośnikiem odwrotnym (\).
Przykład
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
(write-line "Hello World")
(write-line "Welcome to Tutorials Point")
;escaping the double quote character
(write-line "Welcome to \"Tutorials Point\"")
Po wykonaniu kodu zwraca następujący wynik -
Hello World
Welcome to Tutorials Point
Welcome to "Tutorials Point"
Funkcje porównujące ciągi
Liczbowe funkcje i operatory porównania, takie jak <i>, nie działają na łańcuchach. Common LISP udostępnia dwa inne zestawy funkcji do porównywania ciągów znaków w kodzie. W jednym zestawie rozróżniana jest wielkość liter, a w drugim wielkość liter.
Poniższa tabela przedstawia funkcje -
Funkcje uwzględniające wielkość liter | Funkcje bez rozróżniania wielkości liter | Opis |
---|---|---|
string = | ciąg równy | Sprawdza, czy wszystkie wartości operandów są równe, czy nie. Jeśli tak, warunek staje się prawdziwy. |
ciąg / = | ciąg nie równy | Sprawdza, czy wartości operandów są różne, czy nie. Jeśli wartości nie są równe, warunek staje się prawdziwy. |
string < | string-lessp | Sprawdza, czy wartości operandów maleją monotonicznie. |
ciąg> | string-Greaterp | Sprawdza, czy wartości operandów rosną monotonicznie. |
ciąg <= | string-not-Greaterp | Sprawdza, czy wartość dowolnego lewego operandu jest większa lub równa wartości następnego prawego operandu, jeśli tak, warunek staje się prawdziwy. |
ciąg> = | string-not-lessp | Sprawdza, czy wartość dowolnego lewego operandu jest mniejsza lub równa wartości jego prawego operandu. Jeśli tak, warunek staje się prawdziwy. |
Przykład
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
; 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"))
Po wykonaniu kodu zwraca następujący wynik -
NIL
0
NIL
T
NIL
NIL
8
NIL
4
5
Funkcje kontrolujące wielkość liter
W poniższej tabeli opisano funkcje kontrolujące sprawy -
Sr.No. | Opis funkcji |
---|---|
1 | string-upcase Konwertuje ciąg na duże litery |
2 | string-downcase Konwertuje ciąg na małe litery |
3 | string-capitalize Zamienia każde słowo w ciągu na wielką literę |
Przykład
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
(write-line (string-upcase "a big hello from tutorials point"))
(write-line (string-capitalize "a big hello from tutorials point"))
Po wykonaniu kodu zwraca następujący wynik -
A BIG HELLO FROM TUTORIALS POINT
A Big Hello From Tutorials Point
Przycinanie sznurków
W poniższej tabeli opisano funkcje przycinania sznurka -
Sr.No. | Opis funkcji |
---|---|
1 | string-trim Pobiera ciąg znaków jako pierwszy argument i ciąg znaków jako drugi argument i zwraca podłańcuch, w którym wszystkie znaki znajdujące się w pierwszym argumencie są usuwane z ciągu argumentu. |
2 | String-left-trim Pobiera ciąg znaków jako pierwszy argument i ciąg znaków jako drugi argument i zwraca podłańcuch, w którym wszystkie znaki znajdujące się w pierwszym argumencie są usuwane z początku ciągu argumentu. |
3 | String-right-trim Pobiera znak (i) łańcucha jako pierwszy argument i łańcuch jako drugi argument i zwraca podłańcuch, w którym wszystkie znaki znajdujące się w pierwszym argumencie są usuwane z końca łańcucha argumentu. |
Przykład
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
(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 "))
Po wykonaniu kodu zwraca następujący wynik -
a big hello from tutorials point
a big hello from tutorials point
a big hello from tutorials point
big hello from tutorials point
Inne funkcje ciągów
Łańcuchy w LISP-ie są tablicami, a więc także sekwencjami. Omówimy te typy danych w nadchodzących samouczkach. Wszystkie funkcje, które mają zastosowanie do tablic i sekwencji, mają również zastosowanie do łańcuchów. Jednak niektóre powszechnie używane funkcje pokażemy na różnych przykładach.
Obliczanie długości
Plik length funkcja oblicza długość łańcucha.
Wyodrębnianie podciągu
Plik subseq funkcja zwraca podłańcuch (ponieważ ciąg jest również sekwencją) zaczynający się od określonego indeksu i kontynuujący do określonego indeksu końcowego lub końca łańcucha.
Dostęp do znaku w ciągu
Plik char funkcja umożliwia dostęp do poszczególnych znaków łańcucha.
Example
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
(write (length "Hello World"))
(terpri)
(write-line (subseq "Hello World" 6))
(write (char "Hello World" 6))
Po wykonaniu kodu zwraca następujący wynik -
11
World
#\W
Sortowanie i łączenie ciągów
Plik sortfunkcja umożliwia sortowanie łańcucha. Pobiera sekwencję (wektor lub łańcuch) i predykat dwuargumentowy i zwraca posortowaną wersję sekwencji.
Plik merge funkcja przyjmuje dwie sekwencje i predykat i zwraca sekwencję utworzoną przez połączenie dwóch sekwencji, zgodnie z predykatem.
Example
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
;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<))
Po wykonaniu kodu zwraca następujący wynik -
#("Akbar" "Amal" "Anthony")
#("Anju" "Anuj" "Avni" "Rishi" "Zara" "Priyanka")
Odwracanie ciągu
Plik reverse funkcja odwraca ciąg.
Na przykład Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
(write-line (reverse "Are we not drawn onward, we few, drawn onward to new era"))
Po wykonaniu kodu zwraca następujący wynik -
are wen ot drawno nward ,wef ew ,drawno nward ton ew erA
Łączenie ciągów
Funkcja concatenate łączy dwa ciągi. To jest ogólna funkcja sekwencji i musisz podać typ wyniku jako pierwszy argument.
Na przykład Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
(write-line (concatenate 'string "Are we not drawn onward, " "we few, drawn onward to new era"))
Po wykonaniu kodu zwraca następujący wynik -
Are we not drawn onward, we few, drawn onward to new era
Sekwencja jest abstrakcyjnym typem danych w LISP. Wektory i listy to dwa konkretne podtypy tego typu danych. Wszystkie funkcje zdefiniowane w typie danych sekwencyjnych są faktycznie stosowane na wszystkich wektorach i typach list.
W tej sekcji omówimy najczęściej używane funkcje na sekwencjach.
Zanim zaczniemy od różnych sposobów manipulowania sekwencjami (tj. Wektorami i listami), spójrzmy na listę wszystkich dostępnych funkcji.
Tworzenie sekwencji
Funkcja make-sequence umożliwia tworzenie sekwencji dowolnego typu. Składnia tej funkcji to -
make-sequence sqtype sqsize &key :initial-element
Tworzy sekwencję typu sqtype i długości sqsize.
Możesz opcjonalnie określić jakąś wartość używając argumentu : initial-element , wtedy każdy z elementów zostanie zainicjowany tą wartością.
Na przykład Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
(write (make-sequence '(vector float)
10
:initial-element 1.0))
Po wykonaniu kodu zwraca następujący wynik -
#(1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0)
Funkcje ogólne na sekwencjach
Sr.No. | Opis funkcji |
---|---|
1 | elt Umożliwia dostęp do poszczególnych elementów poprzez indeks całkowity. |
2 | length Zwraca długość sekwencji. |
3 | subseq Zwraca podsekwencję, wyodrębniając podsekwencję rozpoczynającą się od określonego indeksu i kontynuując do określonego indeksu końcowego lub końca sekwencji. |
4 | copy-seq Zwraca sekwencję zawierającą te same elementy, co jej argument. |
5 | fill Służy do ustawiania wielu elementów sekwencji na jedną wartość. |
6 | replace Pobiera dwie sekwencje, a pierwsza sekwencja argumentów jest destruktywnie modyfikowana przez kopiowanie do niej kolejnych elementów z drugiej sekwencji argumentów. |
7 | count Pobiera element i sekwencję i zwraca liczbę wystąpień elementu w sekwencji. |
8 | reverse Zwraca sekwencję zawierającą te same elementy argumentu, ale w odwrotnej kolejności. |
9 | nreverse Zwraca tę samą sekwencję zawierającą te same elementy, co sekwencja, ale w odwrotnej kolejności. |
10 | concatenate Tworzy nową sekwencję zawierającą konkatenację dowolnej liczby sekwencji. |
11 | position Pobiera element i sekwencję i zwraca indeks elementu w sekwencji lub nil. |
12 | find Potrzeba przedmiotu i sekwencji. Znajduje element w sekwencji i zwraca go, jeśli nie zostanie znaleziony, zwraca nil. |
13 | sort Pobiera sekwencję i predykat dwuargumentowy i zwraca posortowaną wersję sekwencji. |
14 | merge Pobiera dwie sekwencje i predykat i zwraca sekwencję utworzoną przez połączenie dwóch sekwencji, zgodnie z predykatem. |
15 | map Pobiera funkcję n-argumentową in sekwencji i zwraca nową sekwencję zawierającą wynik zastosowania funkcji do kolejnych elementów sekwencji. |
16 | some Pobiera predykat jako argument i wykonuje iterację po sekwencji argumentów i zwraca pierwszą wartość inną niż NIL zwrócona przez predykat lub zwraca fałsz, jeśli predykat nigdy nie jest spełniony. |
17 | every Przyjmuje predykat jako argument i iteruje po sekwencji argumentów, kończy, zwracając fałsz, gdy tylko predykat zawiedzie. Jeśli predykat jest zawsze spełniony, zwraca prawdę. |
18 | notany Przyjmuje predykat jako argument i iteruje po sekwencji argumentów i zwraca fałsz, gdy tylko predykat zostanie spełniony lub prawda, jeśli nigdy nie jest. |
19 | notevery Przyjmuje predykat jako argument i iteruje sekwencję argumentów i zwraca prawdę, gdy tylko predykat zawiedzie lub fałsz, jeśli predykat jest zawsze spełniony. |
20 | reduce Mapuje pojedynczą sekwencję, stosując najpierw funkcję dwuargumentową do pierwszych dwóch elementów sekwencji, a następnie do wartości zwracanej przez funkcję i kolejne elementy sekwencji. |
21 | search Przeszukuje sekwencję, aby zlokalizować jeden lub więcej elementów spełniających jakiś test. |
22 | remove Pobiera element i sekwencję i zwraca sekwencję z usuniętymi wystąpieniami elementu. |
23 | delete Spowoduje to również pobranie elementu i sekwencji oraz zwrócenie sekwencji tego samego rodzaju, co sekwencja argumentów, która ma te same elementy oprócz elementu. |
24 | substitute Pobiera nowy element, istniejący element i sekwencję oraz zwraca sekwencję z wystąpieniami istniejącego elementu zastąpionymi nowym elementem. |
25 | nsubstitute Pobiera nowy element, istniejący element i sekwencję i zwraca tę samą sekwencję z wystąpieniami istniejącego elementu zastąpionymi nowym elementem. |
26 | mismatch Pobiera dwie sekwencje i zwraca indeks pierwszej pary niedopasowanych elementów. |
Argumenty słów kluczowych funkcji sekwencji standardowej
Argument | Znaczenie | Domyślna wartość |
---|---|---|
:test | Jest to dwuargumentowa funkcja używana do porównywania elementu (lub wartości wyodrębnionej przez: funkcja klucza) z elementem. | EQL |
:klucz | Funkcja jednoargumentowa do wyodrębniania wartości klucza z rzeczywistego elementu sekwencji. NIL oznacza użycie elementu takiego, jaki jest. | ZERO |
:początek | Początkowy indeks (włącznie) podciągów. | 0 |
:koniec | Indeks końcowy (wyłączny) podciągu. NIL oznacza koniec sekwencji. | ZERO |
: od końca | Jeśli prawda, sekwencja będzie wykonywana w odwrotnej kolejności, od końca do początku. | ZERO |
:liczyć | Liczba wskazująca liczbę elementów do usunięcia lub zastąpienia lub NIL, aby wskazać wszystkie (tylko USUŃ i PODSTAW). | ZERO |
Omówiliśmy właśnie różne funkcje i słowa kluczowe, które są używane jako argumenty w tych funkcjach pracujących na sekwencjach. W następnych sekcjach zobaczymy, jak korzystać z tych funkcji na przykładach.
Znajdowanie długości i elementu
Plik length funkcja zwraca długość sekwencji, a funkcja elt funkcja umożliwia dostęp do poszczególnych elementów za pomocą indeksu liczb całkowitych.
Przykład
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
(setq x (vector 'a 'b 'c 'd 'e))
(write (length x))
(terpri)
(write (elt x 3))
Po wykonaniu kodu zwraca następujący wynik -
5
D
Modyfikowanie sekwencji
Niektóre funkcje sekwencji pozwalają na iterowanie sekwencji i wykonywanie pewnych operacji, takich jak wyszukiwanie, usuwanie, liczenie lub filtrowanie określonych elementów bez pisania jawnych pętli.
Poniższy przykład demonstruje to -
Przykład 1
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
(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)))
Po wykonaniu kodu zwraca następujący wynik -
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
Przykład 2
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
(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)
Po wykonaniu kodu zwraca następujący wynik -
(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)
Sortowanie i łączenie sekwencji
Funkcje sortujące pobierają sekwencję i predykat dwuargumentowy i zwracają posortowaną wersję sekwencji.
Przykład 1
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
(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)
Po wykonaniu kodu zwraca następujący wynik -
(1 2 3 3 4 4 5 6 7 8 9)
(9 8 7 6 5 4 4 3 3 2 1)
Przykład 2
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
(write (merge 'vector #(1 3 5) #(2 4 6) #'<))
(terpri)
(write (merge 'list #(1 3 5) #(2 4 6) #'<))
(terpri)
Po wykonaniu kodu zwraca następujący wynik -
#(1 2 3 4 5 6)
(1 2 3 4 5 6)
Predykaty sekwencji
Funkcje every, some, notany i notevery nazywane są predykatami sekwencji.
Te funkcje iterują po sekwencjach i testują predykat Boole'a.
Wszystkie te funkcje przyjmują predykat jako pierwszy argument, a pozostałe argumenty to sekwencje.
Przykład
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
(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)
Po wykonaniu kodu zwraca następujący wynik -
T
T
NIL
NIL
T
Mapping Sequences
Omówiliśmy już funkcje mapowania. Podobniemap function umożliwia zastosowanie funkcji do kolejnych elementów jednej lub większej liczby sekwencji.
Plik map funkcja przyjmuje funkcję n-argumentową in sekwencji i zwraca nową sekwencję po zastosowaniu funkcji do kolejnych elementów sekwencji.
Przykład
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
(write (map 'vector #'* #(2 3 4 5) #(3 5 4 8)))
Po wykonaniu kodu zwraca następujący wynik -
#(6 15 16 40)
Listy były najważniejszą i podstawową strukturą danych złożonych w tradycyjnym LISP-ie. Współczesny Common LISP zapewnia inne struktury danych, takie jak wektor, tablica mieszająca, klasy lub struktury.
Listy to pojedyncze połączone listy. W LISP-ie listy są konstruowane jako łańcuch o prostej strukturze rekordów o nazwiecons połączone ze sobą.
Wady Struktura rekordu
ZA cons jest strukturą rekordu zawierającą dwa komponenty zwane car i cdr.
Wady komórki lub wady to obiekty to pary wartości, które są tworzone za pomocą funkcji cons.
Plik consfunkcja przyjmuje dwa argumenty i zwraca nową komórkę cons zawierającą dwie wartości. Te wartości mogą być odniesieniami do dowolnego rodzaju obiektu.
Jeśli druga wartość nie jest zerem ani nie jest inną komórką wad, wówczas wartości są drukowane jako para z kropkami ujęta w nawiasy.
Dwie wartości w komórce wad nazywane są car i cdr. Plik car funkcja służy do uzyskania dostępu do pierwszej wartości i cdr funkcja służy do uzyskania dostępu do drugiej wartości.
Przykład
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
(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)))))
Po wykonaniu kodu zwraca następujący wynik -
(1 . 2)
(A . B)
(1)
(1 2)
(1 2 3)
(A B C)
A
(B C)
Powyższy przykład pokazuje, w jaki sposób struktury wad mogą zostać użyte do utworzenia pojedynczej listy połączonej, np. Lista (ABC) składa się z trzech komórek wad połączonych ze sobą swoimi cdrs .
Schematycznie można to wyrazić jako -
Listy w LISP
Chociaż komórki wad mogą być używane do tworzenia list, jednak konstruowanie listy z zagnieżdżonej conswywołania funkcji nie mogą być najlepszym rozwiązaniem. Pliklist Funkcja służy raczej do tworzenia list w LISP-ie.
Funkcja listy może przyjmować dowolną liczbę argumentów i ponieważ jest funkcją, oblicza jej argumenty.
Plik first i restfunkcje podają pierwszy element i pozostałą część listy. Poniższe przykłady przedstawiają koncepcje.
Przykład 1
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
(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)))
Po wykonaniu kodu zwraca następujący wynik -
(1 2)
(A B)
(1 NIL)
(1 2 3)
(A B C)
(3 4 A B -8)
((A B) (C D E))
Przykład 2
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
(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))
Po wykonaniu kodu zwraca następujący wynik -
"Hunger Game"
Lista funkcji manipulujących
W poniższej tabeli przedstawiono niektóre często używane funkcje służące do obsługi list.
Sr.No. | Opis funkcji |
---|---|
1 | car Pobiera listę jako argument i zwraca jej pierwszy element. |
2 | cdr Pobiera listę jako argument i zwraca listę bez pierwszego elementu |
3 | cons Pobiera dwa argumenty, element i listę i zwraca listę z elementem wstawionym na pierwszym miejscu. |
4 | list Pobiera dowolną liczbę argumentów i zwraca listę z argumentami jako elementami składowymi listy. |
5 | append Łączy dwie lub więcej list w jedną. |
6 | last Pobiera listę i zwraca listę zawierającą ostatni element. |
7 | member Pobiera dwa argumenty, z których drugi musi być listą, jeśli pierwszy argument jest członkiem drugiego argumentu, a następnie zwraca pozostałą część listy, zaczynając od pierwszego argumentu. |
8 | reverse Pobiera listę i zwraca listę z pierwszymi elementami w odwrotnej kolejności. |
Należy pamiętać, że wszystkie funkcje sekwencji mają zastosowanie do list.
Przykład 3
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
(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))))
Po wykonaniu kodu zwraca następujący wynik -
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)
Łączenie funkcji Car i CDR
Plik car i cdr funkcje i ich kombinacja pozwala na wyodrębnienie dowolnego elementu / członka listy.
Jednak sekwencje funkcji car i cdr można by skrócić, łącząc literę a dla car id dla cdr wewnątrz liter c i r.
Na przykład możemy napisać cadadr, aby skrócić sekwencję wywołań funkcji - car cdr car cdr.
Zatem (cadadr '(a (cd) (efg))) zwróci d
Przykład 4
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
(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)
Po wykonaniu kodu zwraca następujący wynik -
D
A
(3 4)
W LISP-ie symbol to nazwa, która reprezentuje obiekty danych i co ciekawe, jest również obiektem danych.
To, co sprawia, że symbole są wyjątkowe, to fakt, że mają one składnik o nazwie property listlub plist.
Listy nieruchomości
LISP umożliwia przypisywanie właściwości do symboli. Na przykład, mamy obiekt „osoba”. Chcielibyśmy, aby obiekt „osoba” miał takie właściwości, jak imię i nazwisko, płeć, wzrost, waga, adres, zawód itp. Właściwość jest jak nazwa atrybutu.
Lista właściwości jest zaimplementowana jako lista z parzystą liczbą (możliwie zero) elementów. Każda para elementów na liście stanowi wpis; pierwsza pozycja toindicator, a drugi to value.
Kiedy tworzony jest symbol, jego lista właściwości jest początkowo pusta. Właściwości są tworzone przy użyciuget w granicach setf Formularz.
Na przykład poniższe instrukcje pozwalają nam przypisać właściwości tytuł, autora i wydawcę oraz odpowiednie wartości do obiektu o nazwie (symbol) „książka”.
Przykład 1
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
(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)))
Po wykonaniu kodu zwraca następujący wynik -
(GONE WITH THE WIND)
(MARGARET MICHEL)
(WARNER BOOKS)
Różne funkcje listy właściwości umożliwiają przypisywanie właściwości, a także pobieranie, zastępowanie lub usuwanie właściwości symbolu.
Plik getfunkcja zwraca listę właściwości symbolu dla danego wskaźnika. Ma następującą składnię -
get symbol indicator &optional default
Plik getfunkcja wyszukuje listę właściwości danego symbolu dla określonego wskaźnika, jeśli zostanie znaleziona, zwraca odpowiednią wartość; w przeciwnym razie zwracana jest wartość domyślna (lub nil, jeśli nie określono wartości domyślnej).
Przykład 2
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
(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))
Po wykonaniu kodu zwraca następujący wynik -
(GONE WITH THE WIND)
(MARGARET MICHEAL)
(WARNER BOOKS)
Plik symbol-plist funkcja pozwala zobaczyć wszystkie właściwości symbolu.
Przykład 3
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
(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))
Po wykonaniu kodu zwraca następujący wynik -
(CHILDREN 3 SEX FEMALE JOB ACCOUNTANT AGE 43)
Plik remprop funkcja usuwa określoną właściwość z symbolu.
Przykład 4
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
(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))
Po wykonaniu kodu zwraca następujący wynik -
(CHILDREN 3 SEX FEMALE JOB ACCOUNTANT AGE 43)
(CHILDREN 3 SEX FEMALE JOB ACCOUNTANT)
Wektory to jednowymiarowe tablice, a zatem podtyp tablicy. Wektory i listy nazywane są łącznie sekwencjami. Dlatego wszystkie funkcje generyczne sekwencji i funkcje tablicowe, które omówiliśmy do tej pory, działają na wektorach.
Tworzenie wektorów
Funkcja wektorowa umożliwia tworzenie wektorów o stałym rozmiarze z określonymi wartościami. Pobiera dowolną liczbę argumentów i zwraca wektor zawierający te argumenty.
Przykład 1
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
(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)
Po wykonaniu kodu zwraca następujący wynik -
#(1 2 3 4 5)
#(A B C D E)
#(P Q R S T)
Proszę zauważyć, że LISP używa składni # (...) jako dosłownej notacji wektorów. Możesz użyć tej składni # (...) do tworzenia i dołączania wektorów literałów do swojego kodu.
Są to jednak wektory dosłowne, więc modyfikowanie ich nie jest zdefiniowane w LISP. Dlatego do programowania zawsze należy używać rozszerzeniavector funkcja lub bardziej ogólna funkcja make-array do tworzenia wektorów, które planujesz modyfikować.
Plik make-arrayfunkcja jest bardziej ogólnym sposobem tworzenia wektora. Możesz uzyskać dostęp do elementów wektora za pomocąaref funkcjonować.
Przykład 2
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
(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)
Po wykonaniu kodu zwraca następujący wynik -
#(0 1 2 3 4)
#(2 2 2 2 2)
Wskaźnik wypełnienia
Plik make-array Funkcja umożliwia utworzenie wektora o zmiennym rozmiarze.
Plik fill-pointerargument funkcji śledzi liczbę elementów faktycznie przechowywanych w wektorze. Jest to indeks następnej pozycji do wypełnienia po dodaniu elementu do wektora.
Plik vector-pushFunkcja umożliwia dodanie elementu na końcu wektora o zmiennym rozmiarze. Zwiększa wskaźnik wypełnienia o 1.
Plik vector-pop funkcja zwraca ostatnio wciśnięty element i zmniejsza wskaźnik wypełnienia o 1.
Przykład
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
(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)
Po wykonaniu kodu zwraca następujący wynik -
#()
#(A B C)
#(A B C D E)
#(A B)
Wektory są sekwencjami, wszystkie funkcje sekwencji mają zastosowanie do wektorów. Informacje na temat funkcji wektorowych można znaleźć w rozdziale Sekwencje.
Common Lisp nie zapewnia ustawionego typu danych. Jednak zapewnia szereg funkcji, które umożliwiają wykonywanie operacji na liście.
Możesz dodawać, usuwać i wyszukiwać elementy na liście na podstawie różnych kryteriów. Możesz również wykonywać różne operacje na zbiorach, takie jak: suma, przecięcie i różnica zestawów.
Zestawy implementacyjne w LISP
Zestawy, podobnie jak listy, są generalnie implementowane w kategoriach komórek wad. Jednak właśnie z tego powodu operacje na zbiorach stają się coraz mniej wydajne, im większe są zbiory.
Plik adjoinFunkcja pozwala na zbudowanie zestawu. Pobiera element i listę reprezentującą zestaw i zwraca listę reprezentującą zestaw zawierający element i wszystkie elementy z oryginalnego zestawu.
Plik adjoinfunkcja najpierw szuka pozycji na podanej liście, jeśli zostanie znaleziona, to zwraca oryginalną listę; w przeciwnym razie tworzy nową komórkę wad z jejcar jako element i cdr wskazuje oryginalną listę i zwraca tę nową listę.
Plik adjoin funkcja również zajmuje :key i :testargumenty słów kluczowych. Te argumenty służą do sprawdzania, czy element znajduje się na oryginalnej liście.
Ponieważ funkcja adjoin nie modyfikuje oryginalnej listy, aby dokonać zmiany w samej liście, należy albo przypisać wartość zwracaną przez adjoin do oryginalnej listy, albo użyć makra pushnew aby dodać element do zestawu.
Przykład
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
; 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)
Po wykonaniu kodu zwraca następujący wynik -
NIL
(2 1)
(2 1)
(3 2 1)
Sprawdzanie członkostwa
Członkowie grupy funkcji pozwalają sprawdzić, czy element jest członkiem zestawu, czy nie.
Poniżej przedstawiono składnie tych funkcji -
member item list &key :test :test-not :key
member-if predicate list &key :key
member-if-not predicate list &key :key
Te funkcje przeszukują podaną listę pod kątem danej pozycji, która spełnia test. Jeśli nie zostanie znaleziony żaden taki element, funkcja zwracanil. W przeciwnym razie zwracany jest koniec listy z elementem jako pierwszym elementem.
Wyszukiwanie odbywa się tylko na najwyższym poziomie.
Te funkcje mogą być używane jako predykaty.
Przykład
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
(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)))
Po wykonaniu kodu zwraca następujący wynik -
(ZARA RIYAN NUHA)
(2 5/3 'A)
('A 'B 'C)
Ustaw Union
Grupa sumująca funkcji umożliwia wykonanie sumy zestawu na dwóch listach podanych jako argumenty tych funkcji na podstawie testu.
Poniżej przedstawiono składnie tych funkcji -
union list1 list2 &key :test :test-not :key
nunion list1 list2 &key :test :test-not :key
Plik unionfunkcja przyjmuje dwie listy i zwraca nową listę zawierającą wszystkie elementy obecne na jednej z list. Jeśli występują duplikaty, tylko jedna kopia członka zostanie zachowana na liście zwróconej.
Plik nunion funkcja wykonuje tę samą operację, ale może zniszczyć listy argumentów.
Przykład
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
(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)
Po wykonaniu kodu zwraca następujący wynik -
(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))
Proszę zanotować
Funkcja unii nie działa zgodnie z oczekiwaniami bez :test-not #'mismatchargumenty dla listy trzech wektorów. Dzieje się tak, ponieważ listy składają się z komórek wad i chociaż wartości wyglądają dla nas tak samo, rozszerzeniecdrczęść komórek nie pasuje, więc nie są one dokładnie takie same, jak interpreter / kompilator LISP. To jest powód; implementowanie dużych zbiorów nie jest zalecane przy użyciu list. Działa jednak dobrze w przypadku małych zestawów.
Ustaw przecięcie
Grupa funkcji przecięcia pozwala na wykonanie przecięcia na dwóch listach dostarczonych jako argumenty do tych funkcji na podstawie testu.
Poniżej przedstawiono składnie tych funkcji -
intersection list1 list2 &key :test :test-not :key
nintersection list1 list2 &key :test :test-not :key
Te funkcje przyjmują dwie listy i zwracają nową listę zawierającą wszystkie elementy obecne na obu listach argumentów. Jeśli którakolwiek z list zawiera zduplikowane wpisy, nadmiarowe wpisy mogą, ale nie muszą, pojawić się w wyniku.
Przykład
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
(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)
Po wykonaniu kodu zwraca następujący wynik -
(C)
(#(A B) #(5 6 7))
NIL
Funkcja przecięcia jest destrukcyjną wersją przecięcia, tzn. Może zniszczyć oryginalne listy.
Ustaw różnicę
Grupa funkcji różnica nastaw pozwala na wykonanie różnicy nastaw na dwóch listach podanych jako argumenty do tych funkcji na podstawie testu.
Poniżej przedstawiono składnie tych funkcji -
set-difference list1 list2 &key :test :test-not :key
nset-difference list1 list2 &key :test :test-not :key
Funkcja set-diff zwraca listę elementów pierwszej listy, które nie pojawiają się na drugiej liście.
Przykład
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
(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)
Po wykonaniu kodu zwraca następujący wynik -
(A B)
(#(F H))
(#(A B) #(5 6 7) #(F H))
Możesz budować drzewiaste struktury danych z komórek wad, jako listy list.
Aby zaimplementować struktury drzewiaste, będziesz musiał zaprojektować funkcje, które będą przechodzić przez komórki wad w określonej kolejności, na przykład zamówienie przed, w kolejności i po zamówieniu dla drzew binarnych.
Drzewo jako lista list
Rozważmy strukturę drzewa złożoną z komórek wad, które tworzą następującą listę list -
((1 2) (3 4) (5 6)).
Schematycznie można to wyrazić jako -
Funkcje drzewa w LISP
Chociaż przeważnie będziesz musiał napisać własne funkcje drzewa zgodnie z twoimi konkretnymi potrzebami, LISP zapewnia kilka funkcji drzewa, których możesz użyć.
Oprócz wszystkich funkcji list, następujące funkcje działają szczególnie na strukturach drzewiastych -
Sr.No. | Opis funkcji |
---|---|
1 | copy-tree x i opcjonalnie vecp Zwraca kopię drzewa komórek wad x. Rekurencyjnie kopiuje kierunki samochodu i cdr. Jeśli x nie jest komórką wad, funkcja po prostu zwraca x niezmienione. Jeśli opcjonalny argument vecp ma wartość true, ta funkcja kopiuje wektory (rekurencyjnie), a także komórki wad. |
2 | tree-equal xy & klucz: test: test-not: klucz Porównuje dwa drzewa komórek wad. Jeśli x i y są komórkami minusów, ich samochody i cdrs są porównywane rekurencyjnie. Jeśli ani x, ani y nie są komórką wad, są porównywane przez eql lub zgodnie z określonym testem. Funkcja: key, jeśli została określona, jest stosowana do elementów obu drzew. |
3 | subst nowe stare drzewo i klucz: test: test-not: key Zastępuje wystąpienia danej starej pozycji nową pozycją w drzewie , które jest drzewem komórek wad. |
4 | nsubst nowe stare drzewo i klucz: test: test-not: key Działa tak samo jak subst, ale niszczy oryginalne drzewo. |
5 | sublis drzewo alist & klucz: test: test-not: klucz Działa jak subst, z tą różnicą, że pobiera listę skojarzeń starych-nowych par. Każdy element drzewka (po zastosowaniu funkcji: key, jeśli istnieje) jest porównywany z samochodami alist; jeśli pasuje, jest zastępowany przez odpowiedni plik cdr. |
6 | nsublis drzewo alist & klucz: test: test-not: klucz Działa tak samo jak sublis, ale w wersji destrukcyjnej. |
Przykład 1
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
(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)
Po wykonaniu kodu zwraca następujący wynik -
((1 2) (3 4) (5 6))
((1 2) (3 4) (5 6))
((1 2) (3 4) (5 6))
Przykład 2
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
(setq tr '((1 2 (3 4 5) ((7 8) (7 8 9)))))
(write tr)
(setq trs (subst 7 1 tr))
(terpri)
(write trs)
Po wykonaniu kodu zwraca następujący wynik -
((1 2 (3 4 5) ((7 8) (7 8 9))))
((7 2 (3 4 5) ((7 8) (7 8 9))))
Budowanie własnego drzewa
Spróbujmy zbudować własne drzewo, korzystając z funkcji list dostępnych w LISP-ie.
Najpierw stwórzmy nowy węzeł zawierający pewne dane
(defun make-tree (item)
"it creates a new node with item."
(cons (cons item nil) nil)
)
Następnie dodajmy węzeł potomny do drzewa - zajmie to dwa węzły drzewa i doda drugie drzewo jako dziecko pierwszego.
(defun add-child (tree child)
(setf (car tree) (append (car tree) child))
tree)
Ta funkcja zwróci pierwsze dziecko w danym drzewie - weźmie węzeł drzewa i zwróci pierwsze dziecko tego węzła lub nil, jeśli ten węzeł nie ma żadnego węzła potomnego.
(defun first-child (tree)
(if (null tree)
nil
(cdr (car tree))
)
)
Ta funkcja zwróci następny węzeł siostrzany danego węzła - przyjmuje węzeł drzewa jako argument i zwraca odniesienie do następnego węzła siostrzanego lub nil, jeśli węzeł nie ma.
(defun next-sibling (tree)
(cdr tree)
)
Na koniec potrzebujemy funkcji zwracającej informacje w węźle -
(defun data (tree)
(car (car tree))
)
Przykład
W tym przykładzie wykorzystano powyższe funkcje -
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
(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)
Po wykonaniu kodu zwraca następujący wynik -
10
(2 (3 4 5) ((7 8) (7 8 9)))
((1 2 (3 4 5) ((7 8) (7 8 9)) (10)))
Struktura danych tabeli skrótów reprezentuje kolekcję plików key-and-valuepary, które są zorganizowane na podstawie kodu skrótu klucza. Używa klucza, aby uzyskać dostęp do elementów w kolekcji.
Tabela skrótów jest używana, gdy musisz uzyskać dostęp do elementów za pomocą klucza i możesz zidentyfikować użyteczną wartość klucza. Każdy element w tabeli skrótów ma parę klucz / wartość. Klucz służy do uzyskiwania dostępu do elementów w kolekcji.
Tworzenie tabeli skrótów w LISP
W Common LISP tabela skrótów jest zbiorem ogólnego przeznaczenia. Możesz użyć dowolnych obiektów jako klucza lub indeksów.
Kiedy przechowujesz wartość w tabeli skrótów, tworzysz parę klucz-wartość i przechowujesz ją pod tym kluczem. Później możesz pobrać wartość z tablicy skrótów, używając tego samego klucza. Każdy klucz jest mapowany na pojedynczą wartość, chociaż w kluczu można zapisać nową wartość.
Tabele skrótów w LISP można podzielić na trzy typy w zależności od sposobu porównywania kluczy - eq, eql lub equal. Jeśli tablica skrótów jest haszowana na obiektach LISP, klucze są porównywane z eq lub eql. Jeśli skrót tablicy skrótów w strukturze drzewa zostanie porównany przy użyciu równego.
Plik make-hash-tablefunkcja służy do tworzenia tablicy skrótów. Składnia tej funkcji to -
make-hash-table &key :test :size :rehash-size :rehash-threshold
Gdzie -
Plik key argument dostarcza klucz.
Plik :testargument określa sposób porównywania kluczy - powinien mieć jedną z trzech wartości # 'eq, #' eql lub # 'równy lub jeden z trzech symboli eq, eql lub equal. Jeśli nie podano, zakłada się eql.
Plik :sizeargument ustawia początkowy rozmiar tablicy skrótów. Powinna to być liczba całkowita większa od zera.
Plik :rehash-sizeargument określa, o ile należy zwiększyć rozmiar tablicy skrótów, gdy się zapełni. Może to być liczba całkowita większa od zera, czyli liczba wpisów do dodania, lub liczba zmiennoprzecinkowa większa niż 1, która jest stosunkiem nowego rozmiaru do starego. Wartość domyślna tego argumentu jest zależna od implementacji.
Plik :rehash-thresholdargument określa, jak pełna może być tablica skrótów, zanim będzie musiała się rozrosnąć. Może to być liczba całkowita większa od zera i mniejsza niż: rehash-size (w którym to przypadku zostanie przeskalowana za każdym razem, gdy tabela zostanie powiększona) lub może to być liczba zmiennoprzecinkowa z zakresu od zera do 1. Wartość domyślna dla tego argument jest zależny od implementacji.
Możesz także wywołać funkcję make-hash-table bez argumentów.
Pobieranie i dodawanie elementów do tabeli skrótów
Plik gethashfunkcja pobiera element z tablicy skrótów, wyszukując jego klucz. Jeśli nie znajdzie klucza, zwraca nil.
Ma następującą składnię -
gethash key hash-table &optional default
gdzie -
klucz: jest skojarzonym kluczem
hash-table: to tabela skrótów do przeszukania
wartość domyślna: to wartość, która ma zostać zwrócona, jeśli pozycja nie zostanie znaleziona, która wynosi zero, jeśli nie zostanie określony.
Plik gethash funkcja w rzeczywistości zwraca dwie wartości, z których druga jest wartością predykatu, która jest prawdą, jeśli pozycja została znaleziona, a fałszem, jeśli nie znaleziono pozycji.
Aby dodać element do tabeli skrótów, możesz użyć rozszerzenia setf funkcja wraz z gethash funkcjonować.
Przykład
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
(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))
Po wykonaniu kodu zwraca następujący wynik -
(CHARLIE BROWN)
(FREDDIE SEAL)
Usuwanie wpisu
Plik remhashfunkcja usuwa wszelkie wpisy dotyczące określonego klucza w tablicy skrótów. Jest to predykat, który jest prawdziwy, jeśli był wpis, lub fałsz, jeśli go nie było.
Składnia tej funkcji to -
remhash key hash-table
Przykład
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
(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))
Po wykonaniu kodu zwraca następujący wynik -
(CHARLIE BROWN)
(FREDDIE SEAL)
(MARK MONGOOSE)
NIL
Funkcja maphash
Plik maphash funkcja umożliwia zastosowanie określonej funkcji do każdej pary klucz-wartość w tabeli skrótów.
Pobiera dwa argumenty - funkcję i tablicę skrótów i wywołuje funkcję raz dla każdej pary klucz / wartość w tabeli skrótów.
Przykład
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
(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)
Po wykonaniu kodu zwraca następujący wynik -
3 => (MARK MONGOOSE)
2 => (FREDDIE SEAL)
1 => (CHARLIE BROWN)
Wspólny LISP zapewnia liczne funkcje wejścia-wyjścia. Użyliśmy już funkcji formatu i funkcji drukowania do wyjścia. W tej sekcji przyjrzymy się niektórym z najczęściej używanych funkcji wejścia-wyjścia dostępnych w LISP.
Funkcje wejściowe
Poniższa tabela przedstawia najczęściej używane funkcje wejściowe LISP -
Sr.No. | Opis funkcji |
---|---|
1 | read& opcjonalny strumień wejściowy eof-error-p eof-value recursive-p Wczytuje wydrukowaną reprezentację obiektu Lisp ze strumienia wejściowego, buduje odpowiedni obiekt Lisp i zwraca obiekt. |
2 | read-preserving-whitespace& opcjonalny in-stream eof-error-p eof-value recursive-p Jest używany w niektórych szczególnych sytuacjach, w których pożądane jest dokładne określenie, jaki znak zakończył rozszerzony token. |
3 | read-line& opcjonalny strumień wejściowy eof-error-p eof-value recursive-p Czyta wiersz tekstu zakończony znakiem nowej linii. |
4 | read-char& opcjonalny strumień wejściowy eof-error-p eof-value recursive-p Pobiera jeden znak ze strumienia wejściowego i zwraca go jako obiekt znakowy. |
5 | unread-char znak i opcjonalny strumień wejściowy Umieszcza ostatnio odczytany znak ze strumienia wejściowego na początku strumienia wejściowego. |
6 | peek-char& opcjonalny strumień wejściowy typu peek eof-error-p eof-value recursive-p Zwraca następny znak do odczytania ze strumienia wejściowego, bez faktycznego usuwania go ze strumienia wejściowego. |
7 | listen& opcjonalny strumień wejściowy Predykat listen ma wartość true, jeśli istnieje znak natychmiast dostępny ze strumienia wejściowego, i ma wartość false, jeśli nie. |
8 | read-char-no-hang& opcjonalny strumień wejściowy eof-error-p eof-value recursive-p To jest podobne do read-char, ale jeśli nie otrzyma znaku, nie czeka na znak, ale natychmiast zwraca nil. |
9 | clear-input& opcjonalny strumień wejściowy Czyści wszelkie buforowane dane wejściowe skojarzone ze strumieniem wejściowym. |
10 | read-from-string string & opcjonalny eof-error-p eof-value & key: start: end: preserve-whiteespace Pobiera kolejno znaki łańcucha, buduje obiekt LISP i zwraca obiekt. Zwraca również indeks pierwszego znaku w ciągu, który nie został odczytany, lub długość ciągu (lub długość +1), w zależności od przypadku. |
11 | parse-integer string & key: start: end: radix: junk-allowed Sprawdza podłańcuch łańcucha rozdzielany przez: początek i: koniec (domyślnie początek i koniec łańcucha). Pomija białe znaki, a następnie próbuje przeanalizować liczbę całkowitą. |
12 | read-byte binarny strumień wejściowy i opcjonalny eof-error-p eof-value Odczytuje jeden bajt z binarnego strumienia wejściowego i zwraca go w postaci liczby całkowitej. |
Czytanie danych wejściowych z klawiatury
Plik readFunkcja służy do pobierania danych wejściowych z klawiatury. Może nie wymagać żadnych argumentów.
Weźmy na przykład pod uwagę fragment kodu -
(write ( + 15.0 (read)))
Załóżmy, że użytkownik wprowadza 10.2 z wejścia STDIN, zwraca,
25.2
Funkcja read odczytuje znaki ze strumienia wejściowego i interpretuje je, analizując je jako reprezentacje obiektów Lisp.
Przykład
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod -
; 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)
Po wykonaniu kodu zwraca następujący wynik -
Enter Radius: 5 (STDIN Input)
Area: 78.53999
Przykład
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
(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)
)
Po wykonaniu kodu zwraca następujący wynik -
#\W
#\e
#\l
#\c
#\o
#\m
#\e
#\Space
#\t
#\o
#\Space
Funkcje wyjściowe
Wszystkie funkcje wyjściowe w LISP-ie przyjmują opcjonalny argument o nazwie output-stream, do którego dane wyjściowe są wysyłane. Jeśli nie podano lub nil, strumień wyjściowy przyjmuje wartość zmiennej * standardowe wyjście *.
Poniższa tabela przedstawia najczęściej używane funkcje wyjściowe LISP -
Sr.No. | Funkcja i opis |
---|---|
1 | write obiekt & klucz: strumień: ucieczka: podstawa: podstawa: okrąg: ładny: poziom: długość: obudowa: gensym: tablica write obiekt i klucz: strumień: ucieczka: podstawa: podstawa: okrąg: ładny: poziom: długość: obudowa: gensym: tablica: czytelnie: prawy margines: skąpiec-szerokość: linie: pprint-wysyłka Oba zapisują obiekt w strumieniu wyjściowym określonym przez: stream, który domyślnie ma wartość * standard-output *. Inne wartości domyślnie odpowiadają odpowiednim zmiennym globalnym ustawionym do drukowania. |
2 | prin1 obiekt i opcjonalny strumień wyjściowy print obiekt i opcjonalny strumień wyjściowy pprint obiekt i opcjonalny strumień wyjściowy princ obiekt i opcjonalny strumień wyjściowy Wszystkie te funkcje wyprowadzają drukowaną reprezentację obiektu do strumienia wyjściowego . Istnieją jednak następujące różnice -
|
3 | write-to-string obiekt i klucz : ucieczka: podstawa: podstawa: okrąg: ładny: poziom: długość: obudowa: gensym: tablica write-to-string obiekt i klucz: ucieczka: podstawa: podstawa: okrąg: ładny: poziom: długość: sprawa: gensym: tablica: czytelnie: prawy margines: skąpiec-szerokość: linie: pprint-wysyłka prin1-to-string obiekt princ-to-string obiekt Obiekt jest skutecznie drukowany, a znaki wyjściowe są przekształcane w ciąg, który jest zwracany. |
4 | write-char znak i opcjonalny strumień wyjściowy Wyprowadza znak do strumienia wyjściowego i zwraca znak. |
5 | write-string ciąg i opcjonalny strumień wyjściowy i klucz: początek: koniec Zapisuje znaki określonego podciągu łańcucha do strumienia wyjściowego. |
6 | write-line ciąg i opcjonalny strumień wyjściowy i klucz: początek: koniec Działa w ten sam sposób, co napis-napis, ale później wyświetla nowy wiersz. |
7 | terpri& opcjonalny strumień wyjściowy Wyprowadza nową linię do strumienia wyjściowego. |
8 | fresh-line& opcjonalny strumień wyjściowy wypisuje znak nowej linii tylko wtedy, gdy strumień nie jest jeszcze na początku linii. |
9 | finish-output& opcjonalny strumień wyjściowy force-output& opcjonalny strumień wyjściowy clear-output& opcjonalny strumień wyjściowy
|
10 | write-byte całkowity strumień binarny-wyjściowy Zapisuje jeden bajt, wartość liczby całkowitej. |
Przykład
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
; 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)
Po wykonaniu kodu zwraca następujący wynik -
Enter Number : 3456.78 (STDIN Input)
The Number: 3456.78
The Number Doubled: 6913.56
Sformatowane dane wyjściowe
Funkcja formatsłuży do tworzenia ładnie sformatowanego tekstu. Ma następującą składnię -
format destination control-string &rest arguments
gdzie,
- miejsce docelowe to standardowe wyjście
- łańcuch-kontrolny zawiera znaki do wyprowadzenia oraz dyrektywę print.
ZA format directive składa się z tyldy (~), opcjonalnych parametrów przedrostka oddzielonych przecinkami, opcjonalnego dwukropka (:) i modyfikatorów at-sign (@) oraz pojedynczego znaku wskazującego rodzaj dyrektywy.
Parametry przedrostka są zazwyczaj liczbami całkowitymi, zapisanymi jako opcjonalnie podpisane liczby dziesiętne.
Poniższa tabela zawiera krótki opis najczęściej używanych dyrektyw -
Sr.No. | Dyrektywa i opis |
---|---|
1 | ~A Po nim następują argumenty ASCII. |
2 | ~S Po nim następują wyrażenia S. |
3 | ~D Dla argumentów dziesiętnych. |
4 | ~B Dla argumentów binarnych. |
5 | ~O Dla argumentów ósemkowych. |
6 | ~X Dla argumentów szesnastkowych. |
7 | ~C Dla argumentów znakowych. |
8 | ~F Argumenty zmiennoprzecinkowe o stałym formacie. |
9 | ~E Wykładnicze argumenty zmiennoprzecinkowe. |
10 | ~$ Argumenty dolarowe i zmiennoprzecinkowe. |
11 | ~% Drukowany jest nowy wiersz. |
12 | ~* Następny argument jest ignorowany. |
13 | ~? Pośredni. Następny argument musi być łańcuchem, a następny - listą. |
Przykład
Przepiszmy program obliczający pole koła -
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
(defun AreaOfCircle()
(terpri)
(princ "Enter Radius: ")
(setq radius (read))
(setq area (* 3.1416 radius radius))
(format t "Radius: = ~F~% Area = ~F" radius area)
)
(AreaOfCircle)
Po wykonaniu kodu zwraca następujący wynik -
Enter Radius: 10.234 (STDIN Input)
Radius: = 10.234
Area = 329.03473
Omówiliśmy już, jak standardowe wejście i wyjście jest obsługiwane przez wspólny LISP. Wszystkie te funkcje działają również do odczytu i zapisu w plikach tekstowych i binarnych. Jedyną różnicą jest w tym przypadku, że strumień, którego używamy, nie jest standardowym wejściem lub wyjściem, ale strumieniem utworzonym w celu zapisywania lub odczytu z plików.
W tym rozdziale zobaczymy, jak LISP może tworzyć, otwierać, zamykać pliki tekstowe lub binarne do przechowywania danych.
Plik reprezentuje sekwencję bajtów, nie ma znaczenia, czy jest to plik tekstowy czy binarny. Ten rozdział poprowadzi Cię przez ważne funkcje / makra do zarządzania plikami.
Otwieranie plików
Możesz użyć openfunkcja, aby utworzyć nowy plik lub otworzyć istniejący plik. Jest to najbardziej podstawowa funkcja do otwierania pliku. Jednakżewith-open-file jest zwykle wygodniejszy i częściej używany, jak zobaczymy w dalszej części tej sekcji.
Podczas otwierania pliku konstruowany jest obiekt strumieniowy, który reprezentuje go w środowisku LISP. Wszystkie operacje na strumieniu są w zasadzie równoważne z operacjami na pliku.
Składnia dla open funkcja jest -
open filename &key :direction :element-type :if-exists :if-does-not-exist :external-format
gdzie,
Filename argumentem jest nazwa pliku być otwarty lub utworzony.
Do kluczowych argumentów określić rodzaj strumienia i usuwania błędów sposobów.
Plik :direction słowo kluczowe określa, czy strumień powinien obsługiwać dane wejściowe, wyjściowe, czy oba, przyjmuje następujące wartości -
: input - dla strumieni wejściowych (wartość domyślna)
: output - dla strumieni wyjściowych
: io - dla strumieni dwukierunkowych
: probe - do sprawdzania istnienia plików; strumień jest otwierany, a następnie zamykany.
Plik :element-type określa typ jednostki transakcji dla strumienia.
Plik :if-existsargument określa akcję, która ma zostać podjęta, jeśli: direction to: output lub: io, a plik o podanej nazwie już istnieje. Jeśli kierunek to: input lub: probe, ten argument jest ignorowany. Przyjmuje następujące wartości -
: error - sygnalizuje błąd.
: nowa-wersja - tworzy nowy plik o tej samej nazwie, ale większym numerze wersji.
: rename - zmienia nazwę istniejącego pliku.
: rename-and-delete - zmienia nazwę istniejącego pliku, a następnie usuwa go.
: append - dołącza do istniejącego pliku.
: supersede - zastępuje istniejący plik.
nil - nie tworzy pliku lub nawet strumień po prostu zwraca nil, aby wskazać błąd.
Plik :if-does-not-existargument określa akcję, która ma zostać podjęta, jeśli plik o określonej nazwie jeszcze nie istnieje. Przyjmuje następujące wartości -
: error - sygnalizuje błąd.
: create - tworzy pusty plik o podanej nazwie i używa go.
nil - nie tworzy pliku ani nawet strumienia, ale zamiast tego po prostu zwraca nil, aby wskazać błąd.
Plik :external-format argument określa uznany przez implementację schemat reprezentowania znaków w plikach.
Na przykład możesz otworzyć plik o nazwie myfile.txt przechowywany w folderze / tmp jako -
(open "/tmp/myfile.txt")
Pisanie do i czytanie z plików
Plik with-open-fileumożliwia odczyt lub zapis do pliku przy użyciu zmiennej stream związanej z transakcją odczytu / zapisu. Po wykonaniu zadania automatycznie zamyka plik. Jest niezwykle wygodny w użyciu.
Ma następującą składnię -
with-open-file (stream filename {options}*)
{declaration}* {form}*
nazwa_pliku to nazwa pliku, który ma zostać otwarty; może to być ciąg, ścieżka lub strumień.
Te opcje są takie same jak na słowa kluczowe argumenty do funkcji Open.
Przykład 1
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
(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.")
)
Zwróć uwagę, że wszystkie funkcje wejścia-wyjścia omówione w poprzednim rozdziale, takie jak terpri i format, działają przy zapisie do utworzonego tutaj pliku.
Kiedy wykonujesz kod, nic nie zwraca; jednak nasze dane są zapisywane w pliku. Plik:direction :output słowa kluczowe pozwalają nam to zrobić.
Możemy jednak czytać z tego pliku przy użyciu rozszerzenia read-line funkcjonować.
Przykład 2
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
(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)
)
)
Po wykonaniu kodu zwraca następujący wynik -
Welcome to Tutorials Point!
This is a tutorials database
Submit your Tutorials, White Papers and Articles into our Tutorials Directory.
Zamykanie pliku
Plik close funkcja zamyka strumień.
Struktury to jeden z typów danych zdefiniowanych przez użytkownika, który umożliwia łączenie elementów danych różnych typów.
Struktury służą do reprezentowania rekordu. Załóżmy, że chcesz śledzić swoje książki w bibliotece. Możesz chcieć śledzić następujące atrybuty dotyczące każdej książki -
- Title
- Author
- Subject
- Identyfikator książki
Definiowanie struktury
Plik defstructMakro w LISP-ie pozwala zdefiniować abstrakcyjną strukturę rekordu. Plikdefstruct Instrukcja definiuje nowy typ danych z więcej niż jednym składnikiem programu.
Aby omówić format defstructmakro, napiszmy definicję struktury książki. Możemy zdefiniować strukturę książki jako -
(defstruct book
title
author
subject
book-id
)
Proszę zanotować
Powyższa deklaracja tworzy strukturę książki z czterema named components. Tak więc każda stworzona książka będzie przedmiotem tej struktury.
Definiuje cztery funkcje o nazwach tytuł książki, autor książki, temat książki i identyfikator książki, które przyjmą jeden argument, strukturę książki i zwrócą pola tytuł, autor, temat i identyfikator książki obiekt. Te funkcje są nazywaneaccess functions.
Książka symboli staje się typem danych i można to sprawdzić za pomocą typep orzec.
Pojawi się również niejawna funkcja o nazwie book-p, który jest predykatem i będzie prawdziwy, jeśli jego argumentem jest książka, aw przeciwnym razie jest fałszywy.
Inna niejawna funkcja o nazwie make-book zostanie utworzony, czyli plik constructor, który po wywołaniu utworzy strukturę danych z czterema komponentami, nadającą się do użycia z funkcjami dostępu.
Plik #S syntax odnosi się do struktury i można jej używać do czytania lub drukowania wystąpień książki.
Zdefiniowana jest również niejawna funkcja o nazwie copy-book z jednym argumentem. Pobiera obiekt książki i tworzy inny obiekt książki, który jest kopią pierwszego. Ta funkcja nazywa sięcopier function.
Możesz użyć setf na przykład do zmiany elementów książki
(setf (book-book-id book3) 100)
Przykład
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
(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)
Po wykonaniu kodu zwraca następujący wynik -
#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)
Ogólnie rzecz biorąc, pakiet jest zaprojektowany w celu zapewnienia sposobu na oddzielenie jednego zestawu nazw od drugiego. Symbole zadeklarowane w jednym pakiecie nie będą kolidować z tymi samymi symbolami zadeklarowanymi w innym. W ten sposób pakiety zmniejszają konflikty nazw między niezależnymi modułami kodu.
Czytnik LISP przechowuje tabelę wszystkich znalezionych symboli. Kiedy znajdzie nową sekwencję znaków, tworzy nowy symbol i przechowuje w tablicy symboli. Ta tabela nazywa się pakietem.
Bieżący pakiet jest określany przez specjalną zmienną * pakiet *.
W LISP są dwa predefiniowane pakiety -
common-lisp - zawiera symbole wszystkich zdefiniowanych funkcji i zmiennych.
common-lisp-user- używa pakietu common-lisp i wszystkich innych pakietów z narzędziami do edycji i debugowania; w skrócie nazywa się to cl-user
Funkcje pakietu w LISP
Poniższa tabela zawiera najczęściej używane funkcje używane do tworzenia, używania i manipulowania pakietami -
Sr.No. | Funkcja i opis |
---|---|
1 | make-package nazwa-pakietu i klucz: pseudonimy: użyj Tworzy i zwraca nowy pakiet o określonej nazwie pakietu. |
2 | in-package nazwa-pakietu i klucz: pseudonimy: użyj Sprawia, że pakiet jest aktualny. |
3 | in-package Nazwa To makro powoduje, że * pakiet * jest ustawiany na nazwę pakietu, która musi być symbolem lub łańcuchem. |
4 | find-package Nazwa Wyszukuje pakiet. Zwracany jest pakiet o tej nazwie lub pseudonimie; jeśli taki pakiet nie istnieje, find-pakiet zwraca nil. |
5 | rename-package pakiet nowa-nazwa i opcjonalne nowe-pseudonimy zmienia nazwę pakietu. |
6 | list-all-packages Ta funkcja zwraca listę wszystkich pakietów, które obecnie istnieją w systemie Lisp. |
7 | delete-package pakiet Usuwa pakiet. |
Tworzenie pakietu LISP
Plik defpackageFunkcja służy do tworzenia pakietu zdefiniowanego przez użytkownika. Ma następującą składnię -
(defpackage :package-name
(:use :common-lisp ...)
(:export :symbol1 :symbol2 ...)
)
Gdzie,
nazwa-pakietu to nazwa pakietu.
Słowo kluczowe: use określa pakiety, których potrzebuje ten pakiet, tj. Pakiety definiujące funkcje używane przez kod w tym pakiecie.
Słowo kluczowe: export określa symbole, które są zewnętrzne w tym pakiecie.
Plik make-packagefunkcja służy również do tworzenia pakietu. Składnia tej funkcji to -
make-package package-name &key :nicknames :use
argumenty i słowa kluczowe mają takie samo znaczenie jak poprzednio.
Korzystanie z pakietu
Po utworzeniu pakietu możesz użyć kodu z tego pakietu, ustawiając go jako pakiet bieżący. Plikin-package makro sprawia, że pakiet jest aktualny w środowisku.
Przykład
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
(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)
Po wykonaniu kodu zwraca następujący wynik -
Hello! This is Tom's Tutorials Point
Hello! This is Dick's Tutorials Point
Hello! This is Harry's Tutorials Point
Usuwanie pakietu
Plik delete-packagemakro umożliwia usunięcie pakietu. Poniższy przykład demonstruje to -
Przykład
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
(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)
Po wykonaniu kodu zwraca następujący wynik -
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
W powszechnej terminologii LISP wyjątki nazywane są warunkami.
W rzeczywistości warunki są bardziej ogólne niż wyjątki w tradycyjnych językach programowania, ponieważ condition reprezentuje każde wystąpienie, błąd lub nie, które mogą wpływać na różne poziomy stosu wywołań funkcji.
Mechanizm obsługi warunków w LISP-ie obsługuje takie sytuacje w taki sposób, że warunki są używane do sygnalizowania ostrzeżenia (powiedzmy przez wypisanie ostrzeżenia), podczas gdy kod wyższego poziomu na stosie wywołań może kontynuować swoją pracę.
System obsługi stanu w LISP składa się z trzech części -
- Sygnalizacja stanu
- Obsługa stanu
- Zrestartuj proces
Obsługa warunku
Podajmy przykład obsługi warunku wynikającego z dzielenia przez zero, aby wyjaśnić te pojęcia.
Aby poradzić sobie z chorobą, musisz wykonać następujące czynności -
Define the Condition - „Warunek to obiekt, którego klasa wskazuje na ogólny charakter warunku i którego dane instancji zawierają informacje o szczegółach konkretnych okoliczności, które doprowadziły do zasygnalizowania stanu”.
Makro określające warunek służy do definiowania warunku, który ma następującą składnię -
(define-condition condition-name (error) ((text :initarg :text :reader text)) )
Nowe obiekty warunku są tworzone za pomocą makra MAKE-CONDITION, które inicjuje pola nowego warunku na podstawie :initargs argument.
W naszym przykładzie poniższy kod definiuje warunek -
(define-condition on-division-by-zero (error) ((message :initarg :message :reader message)) )
Writing the Handlers- procedura obsługi warunku to kod używany do obsługi sygnalizowanego warunku. Zwykle jest zapisany w jednej z funkcji wyższego poziomu, która wywołuje funkcję błędu. Po zasygnalizowaniu warunku mechanizm sygnalizacji wyszukuje odpowiednią procedurę obsługi na podstawie klasy warunku.
Każdy przewodnik składa się z -
- Specyfikator typu, który wskazuje typ warunku, który może obsłużyć
- Funkcja, która przyjmuje pojedynczy argument, warunek
Kiedy sygnalizowany jest warunek, mechanizm sygnalizacyjny znajduje ostatnio ustanowioną procedurę obsługi, która jest zgodna z typem warunku i wywołuje swoją funkcję.
Makro handler-caseustanawia procedurę obsługi warunków. Podstawowa forma sprawy obsługi -
(handler-case expression error-clause*)
Gdzie każda klauzula błędu ma postać -
condition-type ([var]) code)
Restarting Phase
To jest kod, który faktycznie odzyskuje twój program po błędach, a procedury obsługi warunków mogą następnie obsłużyć warunek, wywołując odpowiedni restart. Kod restartu jest zazwyczaj umieszczany w funkcjach średniego lub niskiego poziomu, a procedury obsługi warunków są umieszczane na wyższych poziomach aplikacji.
Plik handler-bindpozwala na zapewnienie funkcji restartu i pozwala na kontynuowanie na niższym poziomie funkcji bez rozwijania stosu wywołań funkcji. Innymi słowy, przepływ sterowania nadal będzie działał na niższym poziomie.
Podstawowa forma handler-bind wygląda następująco -
(handler-bind (binding*) form*)
Gdzie każde wiązanie to lista następujących elementów -
- typ warunku
- funkcja obsługi jednego argumentu
Plik invoke-restart makro znajduje i wywołuje ostatnio powiązaną funkcję restartu z określoną nazwą jako argumentem.
Możesz mieć wiele ponownych uruchomień.
Przykład
W tym przykładzie demonstrujemy powyższe koncepcje, pisząc funkcję o nazwie funkcja dzielenia, która utworzy warunek błędu, jeśli argument dzielnika ma wartość zero. Mamy trzy anonimowe funkcje, które zapewniają trzy sposoby wyjścia z tego - zwracając wartość 1, wysyłając dzielnik 2 i przeliczając ponownie lub zwracając 1.
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
(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."))
Po wykonaniu kodu zwraca następujący wynik -
error signaled: denominator is zero
Value: 1
error signaled: denominator is zero
Value: 5
error signaled: denominator is zero
Done.
Oprócz „Systemu warunków”, jak omówiono powyżej, Common LISP zapewnia również różne funkcje, które mogą być wywoływane w celu sygnalizowania błędu. Obsługa błędu, gdy zostanie zasygnalizowany, jest jednak zależna od implementacji.
Funkcje sygnalizacji błędów w LISP
Poniższa tabela zawiera często używane funkcje sygnalizujące ostrzeżenia, przerwy, błędy niekrytyczne i krytyczne.
Program użytkownika określa komunikat o błędzie (ciąg znaków). Funkcje przetwarzają ten komunikat i mogą / nie mogą wyświetlać go użytkownikowi.
Komunikaty o błędach należy konstruować, stosując rozszerzenie format function, nie powinien zawierać znaku nowej linii na początku ani na końcu i nie musi wskazywać błędu, ponieważ system LISP zajmie się nimi zgodnie z preferowanym stylem.
Sr.No. | Funkcja i opis |
---|---|
1 | error ciąg formatu i pozostałe argumenty Sygnalizuje fatalny błąd. Nie można kontynuować tego rodzaju błędu; w ten sposób błąd nigdy nie wróci do wywołującego. |
2 | cerror ciąg-ciąg-formatu-błędu-ciąg-formatu i pozostałe argumenty Sygnalizuje błąd i wchodzi do debuggera. Pozwala jednak na kontynuowanie programu z debuggera po usunięciu błędu. |
3 | warn ciąg formatu i pozostałe argumenty wypisuje komunikat o błędzie, ale normalnie nie trafia do debuggera |
4 | break& opcjonalny łańcuch formatu i pozostałe argumenty Drukuje wiadomość i trafia bezpośrednio do debuggera, bez możliwości przechwycenia przez zaprogramowane funkcje obsługi błędów |
Przykład
W tym przykładzie funkcja silnia oblicza silnię liczby; jeśli jednak argument jest ujemny, powoduje to wystąpienie błędu.
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
(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))
Po wykonaniu kodu zwraca następujący wynik -
120
*** - -1 is a negative number.
Common LISP wyprzedził rozwój programowania obiektowego o kilka dekad. Jednak na późniejszym etapie została do niego włączona orientacja obiektowa.
Definiowanie klas
Plik defclassmakro umożliwia tworzenie klas zdefiniowanych przez użytkownika. Ustanawia klasę jako typ danych. Ma następującą składnię -
(defclass class-name (superclass-name*)
(slot-description*)
class-option*))
Boksy to zmienne przechowujące dane lub pola.
Opis slotu ma postać (nazwa-slotu opcja-slotu *), gdzie każda opcja jest słowem kluczowym, po którym następuje nazwa, wyrażenie i inne opcje. Najczęściej używane opcje slotów to -
:accessor nazwa-funkcji
:initform wyrażenie
:initarg symbol
Na przykład zdefiniujmy klasę Box z trzema długościami, szerokości i wysokości szczelin.
(defclass Box ()
(length
breadth
height)
)
Zapewnienie kontroli dostępu i odczytu / zapisu do gniazda
O ile gniazda nie mają wartości, do których można uzyskać dostęp, odczytać lub zapisać, klasy są dość bezużyteczne.
Możesz określić accessorsdla każdego gniazda podczas definiowania klasy. Weźmy na przykład naszą klasę Box -
(defclass Box ()
((length :accessor length)
(breadth :accessor breadth)
(height :accessor height)
)
)
Możesz również określić oddzielne accessor nazwy do czytania i pisania slotu.
(defclass Box ()
((length :reader get-length :writer set-length)
(breadth :reader get-breadth :writer set-breadth)
(height :reader get-height :writer set-height)
)
)
Tworzenie instancji klasy
Funkcja ogólna make-instance tworzy i zwraca nowe wystąpienie klasy.
Ma następującą składnię -
(make-instance class {initarg value}*)
Przykład
Utwórzmy klasę Box z trzema szczelinami, długością, szerokością i wysokością. Do ustawienia wartości w tych polach użyjemy trzech metod dostępu do gniazd.
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
(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))
Po wykonaniu kodu zwraca następujący wynik -
Length of the Box is 10
Breadth of the Box is 10
Height of the Box is 5
Definiowanie metody klasowej
Plik defmethodmakro umożliwia zdefiniowanie metody wewnątrz klasy. Poniższy przykład rozszerza naszą klasę Box o metodę o nazwie volume.
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
(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))
Po wykonaniu kodu zwraca następujący wynik -
Length of the Box is 10
Breadth of the Box is 10
Height of the Box is 5
Volume of the Box is 500
Dziedzictwo
LISP umożliwia zdefiniowanie obiektu w kategoriach innego obiektu. To się nazywainheritance.Możesz utworzyć klasę pochodną, dodając funkcje, które są nowe lub inne. Klasa pochodna dziedziczy funkcjonalności klasy nadrzędnej.
Poniższy przykład wyjaśnia to -
Przykład
Utwórz nowy plik kodu źródłowego o nazwie main.lisp i wpisz w nim następujący kod.
(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))
Po wykonaniu kodu zwraca następujący wynik -
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