Lisp
Содержание:
Синтаксис[править]
Основной механизм Лиспа, инкапсулированная в список определяющая голова списка, и подключенный к ней хвост списка, который рекурсивно также может быть списком (ламбда-исчисление). Лисп-машина способна воспринимать каждый поступающий на неё список на самом абстрактном уровне, например как мета-Лисп-машину модифицирующую воспринимающую машину. В такой динамичной, высокоабстрактной среде можно реализовать как строго-научные системы, так и неисчислимое множество программистских трюков и генераторов всевозможных машин.
Любая программа на Лиспе состоит из последовательности выражений (форм). Результат работы программы состоит в вычислении этих выражений. Все выражения записываются в виде списков — одной из основных структур Лиспа, поэтому они могут легко быть созданы посредством самого языка. Это позволяет создавать программы, изменяющие другие программы или макросы, позволяющие существенно расширить возможности языка.
Список является последовательностью элементов любого рода, в том числе других списков. Например, состоит из целого числа, рациональной дроби, символа foo и указателя на функцию сложения. Выражения представляются списками в префиксной записи: первый элемент должен быть формой, то есть функцией, оператором, макросом или специальным оператором; прочие элементы суть аргументы, передаваемые форме для обработки. Функция возвращает список состоящий из её аргументов: например, возвращает список, упомянутый ранее. Если некоторые элементы являются выражениями, то сначала вычисляется их значение: возвращает . Арифметические операторы действуют так же, например выдаёт 10.
Специальные операторы позволяют управлять последовательностью вычислений. С их помощью реализуются ветвление и циклы. Оператор позволяет вычислить одно из двух выражений в зависимости от выполнения условия, которое тоже является выражением. Если его результат не , то вычисляется первый аргумент, иначе вычисляется второй. Например, всегда возвращает .
Символьная природа языка (то есть отсутствие в символьном пространстве традиционной метрической геометрии расстояний, последовательностей и т.д) позволяет легко и продуктивно распараллеливать Лисп-процессы. (Что нашло использование в сверхмощных телекоммуникационных, сетевых Лисп-системах.)
При всей мощности символьного подхода, он не может занять место контининтуальных (нейро) систем (не-машин!) нейрокомпьютеров. Т. н. проблема образования категорий из сенсорных данных. (см. Harnad).
Интервью с Nenad Rakocevic о языке Red, преемнике Rebol
Перевод
После нашего последнего интервью с Brian McKenna для This is not a Monad tutorial мы взяли интервью у Nenad Rakocevic, создателя языка программирования Red.
С моей субъективной точки зрения Red и Rebol довольно странные создания! Но не поймите меня неправильно, это не значит ничего плохого. Например, я не знаю многих высокоуровневых языков с такими возможностями как встраиваемые DSL для стандартного низкоуровневого программирования или имеющего 50 встроенных типов. Вам следует попробовать его, вы найдете много интересных идеи в программировании на Red.
Пишите мне на твиттер @unbalancedparen свои комментарии или предложения по интервью для This is not a Monad tutorial. Оставайтесь с нами!Расскажите нам немного о появлении Red. Для чего он был создан?
Коды возврата vs исключения: взгляд с колокольни
Просмотрев пост Коды возврата vs исключения и комментарии к нему, я заметил, что в обсуждении упущена одна нить, краткий тезис которой следующий: в некоторых языках такая проблема даже не стоИт, т.к. вопрос «что выбрать, коды возврата или исключения» в таком языке является низкоуровневым. Как, например, не стоит вопрос, каким образом реализовать конструкцию «foreach». Т.к. для программиста, использующего тот же «foreach», нет никакой разницы, использовали ли создатели языка while или for или что-то еще в имплементации данного оператора. Главное это паттерн, который представляет собой этот самый оператор.
Хватит рассуждать про foreach. Покажу непосредственно на примере два очень похожих друг на друга оператора, один из которых использует в качестве реализации «исключения», другой — «коды возврата».
GUI в Common Lisp — еще один миф
Бытует расхожее мнение, что в Common Lisp нет или же плохая поддержка графики. Это еще один миф из серии, что Lisp — это язык только для подсчета факториалов. На самом деле, как и в большинстве других прикладных сфер общего назначения (например, веб, форматы передачи данных, взаимодействие с БД и т.д.) в Lisp-среде есть полный спектр библиотек и тулкитов для всех основных платформ с разными уровнями абстракции.
Linux/Unix
Базовой библиотекой для графики из Common Lisp под Unix является CLX. Это аналог xlib, т.е. низкоуровневый клиент, напрямую общающийся по X-протоколу.
Кроме того, есть обертки для основных графических фреймворков разной степени зрелости: LTK, CL-GTK2, CommonQt, CL-CAIRO2. Лично мне доводилось иметь дело с LTK, и работа с ним тривиальна. Хороший пример приложения, его использующего — простой и удобный Lisp-редактор/REPL ABLE.
Windows
Кроме возможности использовать кросс-платформенные фреймворки из прошлого раздела, есть еще LispWorks CAPI, о котором только положительные отзывы. Единственная особенность заключается в том, что, как и большинство профессиональных сред разработки на любых языках под Windows, LispWorks стоит довольно дорого (ок. 1200 $), а CAPI доступна только в профессиональной версии. (Впрочем, попробовать его можно и в trial версии).
Также есть CL-OPENGL, которая, разумеется, кросс-платформенная.
Afterword
This announcement is targeted mainly at those who are already «enlightened» about RDF triple stores and were eagerly waiting for a chance to try agraph. 🙂 I hope that it provides a good starting point for you to actually do it. I believe, the agraph download webpage gives enough guidance regarding installing it either on your machine or running it from the AWS Marketplace.
As I said, there will be another post (for now, unclear when) that will be an introduction to RDF cabilities for those developers who are still «in ignorance» about the possibilities that triple stores may open for their applications. Stay tuned…
Синтаксис
Common Lisp использует S-выражения для обозначения как кода, так и данных. Вызовы функций и макросов являются списками, где первый элемент, или голова списка, является именем функции, а остальные, — «хвост» списка, — аргументами.
(+ 2 2) ; складывает 2 и 2, возвращая результат 4.
(- 8 3) ; вычитает из восьми три, возвращая результат 5.
(reverse "Здравствуй, мир!") ; переворачивает строку, возвращая «"!рим ,йувтсвардЗ"»
;; определения переменных и функций:
(defvar *x*) ; Определяет переменную *x*, не присваивая ей какого-либо
; значения. Две звёздочки, являющиеся частью имени — принятый
; стандарт для именования глобальных переменных.
(setf *x* 42.1) ; присваивает переменной *x* значение с плавающей запятой 42,1.
(defun square (x) ; Определение функции square, получающей один аргумент
(* x x)) ; и возвращающей его квадрат.
(square *x*) ; вызывает функцию square, передавая ей значение переменной *x*
; и возвращает её квадрат (1772,41).
(let ((a 3)(b 4)) (+ a b)) ; Специальная форма let создаёт локальные переменные, присваивает им значения
; (в данном случае переменной a присваивается значение 3, а b - 4),
; после чего вычисляет и возвращает результат функции
; (в данном случае 7). Переменные локальны, следовательно
; попытка посчитать значение (+ a b) вне тела let приведёт к ошибке.
Symbolic SPARQL
Another thing worth some attention in this client is its symbolic SPARQL facility that allows generating SPARQL requests from s-expressions. Query generation from sexps is a common Lisp trick that can be found in such libraries as CLSQL & co. However, the implementation I came up with is, from my point of view, much simpler.
Here are a few simple examples that give a general impression of symbolic SPARQL:
The function uses a very simple evaluation rule. It will print any symbols as is, while lists are processed recursively in 3 possible ways depending on the first element:
- a keyword in first position means that custom rules should be invoked;
- any other symbol causes the list to be treated as a tiples pattern containing subject, predicate(s), and object(s);
- another list invokes recursive processing.
Now, the custom rules are defined as methods of a generic function , which makes this mechanism quite extensible. Let’s see an example SPARQL sexps and the custom rules that were used to handle it:
The sexp-based form of SPAQRL queries may seem unusual, but it is much more convenient and powerful than the standard string format:
- it is more convenient to edit;
- passing variables is easy;
- and you can write function and macros to construct these expressions from parts, which is very rough and error-prone using the string-based format.
I considered implementing symbolic SPARQL ever since I started working with it as programmatically filling string templates is so primitive. Finally, I’ve found time to realize this idea!
Handling a Condition
Let us take up an example of handling a condition arising out of divide by zero condition, to explain the concepts here.
You need to take the following steps for handling a condition −
Define the Condition − «A condition is an object whose class indicates the general nature of the condition and whose instance data carries information about the details of the particular circumstances that lead to the condition being signalled».
The define-condition macro is used for defining a condition, which has the following syntax −
(define-condition condition-name (error) ((text :initarg :text :reader text)) )
New condition objects are created with MAKE-CONDITION macro, which initializes the slots of the new condition based on the :initargs argument.
In our example, the following code defines the condition −
(define-condition on-division-by-zero (error) ((message :initarg :message :reader message)) )
-
Writing the Handlers − a condition handler is a code that are used for handling the condition signalled thereon. It is generally written in one of the higher level functions that call the erroring function. When a condition is signalled, the signalling mechanism searches for an appropriate handler based on the condition’s class.
Each handler consists of −
- Type specifier, that indicates the type of condition it can handle
- A function that takes a single argument, the condition
When a condition is signalled, the signalling mechanism finds the most recently established handler that is compatible with the condition type and calls its function.
The macro handler-case establishes a condition handler. The basic form of a handler-case −
(handler-case expression error-clause*)
Where, each error clause is of the form −
condition-type () code)
Restarting Phase
This is the code that actually recovers your program from errors, and condition handlers can then handle a condition by invoking an appropriate restart. The restart code is generally place in middle-level or low-level functions and the condition handlers are placed into the upper levels of the application.
The handler-bind macro allows you to provide a restart function, and allows you to continue at the lower level functions without unwinding the function call stack. In other words, the flow of control will still be in the lower level function.
The basic form of handler-bind is as follows −
(handler-bind (binding*) form*)
Where each binding is a list of the following −
- a condition type
- a handler function of one argument
The invoke-restart macro finds and invokes the most recently bound restart function with the specified name as argument.
You can have multiple restarts.
Example
In this example, we demonstrate the above concepts by writing a function named division-function, which will create an error condition if the divisor argument is zero. We have three anonymous functions that provide three ways to come out of it — by returning a value 1, by sending a divisor 2 and recalculating, or by returning 1.
Create a new source code file named main.lisp and type the following code in it.
(define-condition on-division-by-zero (error)
((message :initarg :message :reader message))
)
(defun handle-infinity ()
(restart-case
(let ((result 0))
(setf result (division-function 10 0))
(format t "Value: ~a~%" result)
)
(just-continue () nil)
)
)
(defun division-function (value1 value2)
(restart-case
(if (/= value2 0)
(/ value1 value2)
(error 'on-division-by-zero :message "denominator is zero")
)
(return-zero () 0)
(return-value (r) r)
(recalc-using (d) (division-function value1 d))
)
)
(defun high-level-code ()
(handler-bind
(
(on-division-by-zero
#'(lambda (c)
(format t "error signaled: ~a~%" (message c))
(invoke-restart 'return-zero)
)
)
(handle-infinity)
)
)
)
(handler-bind
(
(on-division-by-zero
#'(lambda (c)
(format t "error signaled: ~a~%" (message c))
(invoke-restart 'return-value 1)
)
)
)
(handle-infinity)
)
(handler-bind
(
(on-division-by-zero
#'(lambda (c)
(format t "error signaled: ~a~%" (message c))
(invoke-restart 'recalc-using 2)
)
)
)
(handle-infinity)
)
(handler-bind
(
(on-division-by-zero
#'(lambda (c)
(format t "error signaled: ~a~%" (message c))
(invoke-restart 'just-continue)
)
)
)
(handle-infinity)
)
(format t "Done."))
When you execute the code, it returns the following result −
error signaled: denominator is zero Value: 1 error signaled: denominator is zero Value: 5 error signaled: denominator is zero Done.
Apart from the ‘Condition System’, as discussed above, Common LISP also provides various functions that may be called for signalling an error. Handling of an error, when signalled, is however, implementation-dependent.
Tree Functions in LISP
Although mostly you will need to write your own tree-functionalities according to your specific need, LISP provides some tree functions that you can use.
Apart from all the list functions, the following functions work especially on tree structures −
| Sr.No. | Function & Description |
|---|---|
| 1 |
copy-tree x & optional vecp It returns a copy of the tree of cons cells x. It recursively copies both the car and the cdr directions. If x is not a cons cell, the function simply returns x unchanged. If the optional vecp argument is true, this function copies vectors (recursively) as well as cons cells. |
| 2 |
tree-equal x y & key :test :test-not :key It compares two trees of cons cells. If x and y are both cons cells, their cars and cdrs are compared recursively. If neither x nor y is a cons cell, they are compared by eql, or according to the specified test. The :key function, if specified, is applied to the elements of both trees. |
| 3 |
subst new old tree & key :test :test-not :key It substitutes occurrences of given old item with new item, in tree, which is a tree of cons cells. |
| 4 |
nsubst new old tree & key :test :test-not :key It works same as subst, but it destroys the original tree. |
| 5 |
sublis alist tree & key :test :test-not :key It works like subst, except that it takes an association list alist of old-new pairs. Each element of the tree (after applying the :key function, if any), is compared with the cars of alist; if it matches, it is replaced by the corresponding cdr. |
| 6 |
nsublis alist tree & key :test :test-not :key It works same as sublis, but a destructive version. |
Example 1
Create a new source code file named main.lisp and type the following code in it.
(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)
When you execute the code, it returns the following result −
((1 2) (3 4) (5 6)) ((1 2) (3 4) (5 6)) ((1 2) (3 4) (5 6))
Example 2
Create a new source code file named main.lisp and type the following code in it.
(setq tr '((1 2 (3 4 5) ((7 8) (7 8 9))))) (write tr) (setq trs (subst 7 1 tr)) (terpri) (write trs)
When you execute the code, it returns the following result −
((1 2 (3 4 5) ((7 8) (7 8 9)))) ((7 2 (3 4 5) ((7 8) (7 8 9))))
Liscript — web REPL: поцелуи, велосипеды и экскаваторы
Некоторое время назад я написал интерпретатор лиспоподобного языка, который назвал Liscript. Опубликовал несколько статей на Хабре, посвященных особенностям реализации ядра, TCO, GUI, REPL-ботов и т.п. Недавно добавил web-интерфейс REPL-у (ссылка в конце статьи).
При чем здесь поцелуи и экскаваторы? Думаю, большинству известны такие аббревиатуры, как KISS (keep it simple stupid — делай это проще, дурачок), YAGNI (You ain’t gonna need it — Вам это не понадобится), а также высказывания людей разной степени великости про архитектурных астронавтов, «все должно быть сделано так просто, насколько возможно, но не проще», и т.п.
Допустим, перед вами стоит задача — выкопать яму. Какие есть варианты решения? Взять лопату и выкопать самому — дешево и сердито, но долго и возможно неоптимально (зависит от вашего уровня владения лопатой и размеров ямы). Отдать на аутсорс таджикам (не будем рассматривать здесь этот вариант, хотя я должен был его упомянуть). Взять экскаватор — быстро и эффективно, но затратно: бензин/аренда, плюс не факт, что он проедет в вашу садовую калитку, значит надо сносить/восстанавливать забор и т.д. Также, необходимо определиться с моделью (порой из 100500 вариантов), а если вы будете управлять им самостоятельно, надо разобраться во всех его рычагах и педалях.
Разумеется, если вы — профессиональный экскаваторщик, копаете по 200 ям за день, или вы стремитесь им стать, а изначальная задача (вырыть яму) нужна вам не сама по себе, а как тренировка или демонстрация ваших умений, тогда выбор очевиден (остается разве что вопрос модели). Но даже профессионал возьмет лопату, сажая цветы.
В общем, про выбор инструментов под задачи, и конкретные (подозреваю, что спорные) решения, которые я выбирал в процессе реализации проекта, под катом.
Lisp со вкусом Pascal или 8501-й язык программирования
Из песочницы
Некоторое время назад (года три) решил почитать учебник по Лиспу. Без всякой конкретной цели, просто ради общего развития и возможности шокировать собеседников экзотикой (один раз кажется, даже получилось).
Но при ближайшем рассмотрении Лисп оказался действительно мощным, гибким и как, ни странно, полезным в «быту». Все мелкие задачи автоматизации быстро перекочевали в скрипты на Лиспе, а так же появились возможности для автоматизации более сложной.
Здесь стоить отметить, что под «возможностью автоматизации» я подразумеваю ситуацию, когда суммарное время на написание и отладку программы меньше, чем время, затрачиваемое на решение той же задачи вручную.
Пол Грэм написал не одну статью и даже книгу о преимуществах Лиспа. На момент написания этой статьи Lisp занимает 33-е место в рейтинге TOIBE (в три раза мертвее мёртвого Delphi). Возникает вопрос: почему язык так мало распространён если он так удобен? Приблизительно два года использования дали несколько намёков на причины.
Как я стал лучше программировать
Перевод
Автор статьи — Джеймс Лонг, один из создателей Firefox Developer Tools
Несколько человек на React Conf спросили у меня совета, как программировать лучше. По какой-то причине люди видят во мне продвинутого программиста, к советам которого стоит прислушаться. Я подумал, стоит записать «ментальную модель» того, как я подходил к программированию на протяжении всех лет.
Некоторая информация: мне 32 года и 10 лет твёрдого опыта. Наверное, только в последние пару лет я приобрёл уверенность в том, что делаю. Но даже теперь я продолжаю сомневаться в себе. Дело в том, что это чувство никогда не уходит, так что старайтесь не обращать на него внимания, продолжайте хаки и накапливайте опыт.
Сразу скажу, что здесь всего несколько советов для улучшения своих способностей. В итоге вам нужно самим выяснить, что лучше всего подходит именно вам. Это просто те вещи, которые я посчитал полезными.
Практическое функциональное программирование
Перевод
Текст статьи взят из презентации, которую я показывал в LinkedIn в2016 году. В презентации была предпринята попытка объяснить функциональное программирование без использования таких понятий, как «монады», «неизменность» или «побочные эффекты». Вместо этого она фокусируется на том, как размышления о композиции могут сделать вас лучшим программистом, независимо от того, какой язык вы используете.
40 лет назад, 17 октября 1977 года, премия Тьюринга была вручена Джону Бэкусу за его вклад в разработку систем программирования высокого уровня, прежде всего языка программирования Fortran. Всем лауреатам премии Тьюринга предоставляется возможность выступить с лекцией по выбранной ими теме в течение года, в котором они получили премию. Как создатель языка программирования Фортран, можно было ожидать, что Бэкус выступит с лекцией о преимуществах Фортрана и будущих разработках в этом языке. Вместо этого он прочитал лекцию под названием «Можно ли освободить программирование от стиля фон Неймана»? в котором он критиковал некоторые из основных языков того времени, включая Фортран, за их недостатки. Он также предложил альтернативу: функциональный стиль программирования.
Язык Scheme
Если здесь HD-режим недоступен, значит нужно перейти на vimeo.
Знакомство с языком программирования Scheme в рамках проекта CS Student. В видео мы познакомимся с некоторыми простыми типами данных, порядком обработки процедур и выполним парочку заданий из моего курса Programming Paradigms. Во второй части будет решение задачек немного посложнее и знакомство с другими возможностями языка. Ссылки:
- Scheme на Wikipedia (англ)
- Самоучитель Scheme на нашей Wiki
- Оригинал самоучителя «Teach Yourself Scheme in Fixnum Days»
- Полный текст задания
Интерпретаторы
- PLT Scheme (Dr. Scheme)
- MIT/GNU Scheme
- Kawa
- 3DScheme
- Bigloo
- Schemeway
- PVTS
λ-исчисление и LISP
Из песочницы
Типичный любитель λ-исчисления.
Однажды, бороздя просторы интернета, я наткнулся на новость о лиспоподобном, встраиваемом языке программирования Shen. Раньше у них на главной странице висел пример кода на Лиспе. В примере авторы описывают абстрактную коллекцию и функции работы с ними.
Так уж совпало, что я являюсь тайным почитателем функционального программирования, а значит, в некоторой степени, лямбда-исчисления, которое состоит почти полностью из абстракций. Захотелось мне написать что-то максимально абстрактное и, что бы усложнить себе задачу, я решил, что встроенные типы данных это для слабых прикладников, а реализовать все нужно непременно в терминах кодирования Чёрча.
С лямбда исчислением я был знаком очень поверхностно, так как почти всегда статьи об этом разделе математики состоят полностью из формул, которые никак не попробовать на практике не представляется возможным. Поэтому пришла идея разобраться с ним на практике, и возможно привлечь на темную сторону силы адептов машины Тьюринга ООП.
Если вас не пугает Lisp, много лямбд и y-combinator (не тот, который с новостями),
Io Language: Синтаксис
В продолжение серии статей про Io, хотелось бы отдельно остановиться на синтаксисе языка.
В двух словах синтаксис Io можно описать так: простой, но странный. Многих удивит такой подход, но, спешу заверить, как и в Lisp, синтаксис неудобен только при взгляде со стороны. С эстетической точки зрения можно отдельно выделить тот факт, что многие прогаммы на Io внешне кажутся излишне разреженными, на экране остается много «лишнего», незаполненного полезным кодом места, это ощущение меня не покидает довольно давно, я пока не придумал, как полностью избавиться от этого вывиха мозга, я просто придумал для себя некоторые правила оформления кода, при соблюдении которых эффект значительно сморщивается:
- Длина строки не должна превышать 80 символов (Привет, text mode!)
- Между клонированием объекта и описанием тела нового должна быть пустая строка
- Методы и свойства группируются
- Без лишней необходимости скобки на новую строку не переносятся
Ну что, поехали. Вынос мозга №2.
tomb
I preface this by saying that I am not a crypto nerd. Probably don’t use this in production anywhere, and definitely don’t use it anywhere security is an actual concern. I’m not aware of a way to back out the initial plaintext, but you should take Schneier’s advice about what to think of that.
That being said, I’ve got this toy project with a -shaped hole in its list, and I may as well try something.
Principles first.
- Sane defaults — We don’t want to make the user do any more work than they have to. Which means that the minimal call to the top level interface should be something that goes rather than needing the user to generate their own salt, specify a cipher or do any type conversions.
- Flexible implementation — We shouldn’t assume a particular salting strategy, input size, or cipher. We need to limit ourselves to mode, because changing that is deep magic that I’m not getting anywhere near without a deeper understanding.
- Use Crypto Primitives — Speaking of deep magic, we’re not writing anything ourselves from the bytes up. is a thing, and it works well if sometimes counter-intuitively, and I fully intend to take advantage.
With that out of the way, here’s , which is sort of like .
The core function is . It takes a (your password/passphrase), and optionally also , and . If you don’t pass in any of those, it chooses sane defaults, including using / to generate a secure random salt value.
The first thing we do is the and values, convert the result to an , then hash it. Hashing it using some secure digest method that produces the appropriate number of bytes to be used as a for the chosen .
It looks like is good enough for most of the capable ciphers in , but and need larger keys than it provides, so we use other approaches when using those ciphers. I don’t want to make it too easy to use weaker ciphers, so I don’t bother using hashes that result in keys smaller than .
Next up, we initialize an cipher with the appropriate base state, and allocate an output to stuff the results in. Then we use the initialized to our input hash (complete with ) and put the results in . Once that’s done, we the output with the same settings, changing out the key each time. The thing we’re encrypting the first time though is the (with itself), and every subsequent layer of encryption also uses itself as the key.
Once we’ve done this, we stitch everything together into a string that contains documentation about its’ creation.
takes a string and an ed string, and returns a yay or nay about whether they match. It does this by decomposing the ed string in a way that lets it figure out what arguments to pass to , and does so on the input string.
HomeLisp
Недавно мой отец закончил разработку своей реализации языка LISP под Windows. Проект называется HomeLisp. Для того, чтобы проект был доступен любителям LISP-а был создан простейший сайт, куда будут выкладываться обновления, документация и где отец будет отвечать на вопросы пользователей. Собственно передаю ему слово.
HomeLisp – простой, свободно распространяемый Лисп на платформе Windows!
Среди языков программирования, разработанных к настоящему времени, имеется один совершенно уникальный. Это язык Лисп.
Идея, лежащая в основе Лиспа, чрезвычайно красива: все конструкции языка (программы и данные) представляются с помощью единой универсальной структуры – списка. Методология программирования на Лиспе в корне отличается от методологии программирования на традиционных языках (типа Си или Паскаля).
Лисп-программист не разбивает алгоритм на отдельные элементарные шаги, не использует циклы, ветвления, присвоения значений переменным, а строит функцию, решающую поставленную задачу. При этом структура функции почти дословно соответствует математической формулировке решаемой задачи.
Подобная методология сейчас называется функциональным программированием. Лисп – первый функциональный язык программирования и, вероятно, первый язык, который явно приветствовал использование рекурсии.
Языки, появившиеся после Лиспа, превзошли Лисп по изобразительным возможностям… но по соотношению «сила/простота» рекорд Лиспа не побит до сих пор!
Лисп – абсолютно не попсовый, элитарный язык программирования. И не удивительно, что хотя с момента его изобретения Дж. МакКарти прошло уже более 50 лет, Лисп жив по сей день! Далеко не каждому языку суждена столь долгая жизнь, – где сейчас Алгол-60 (которому пророчили большое будущее)? А по Лиспу издаются книги (к сожалению, не на русском языке…); появляются новые и новые реализации Лиспа на разных аппаратных платформах.
Logical Operations on Boolean Values
Common LISP provides three logical operators: and, or, and not that operates on Boolean values. Assume A has value nil and B has value 5, then −
| Operator | Description | Example |
|---|---|---|
| and | It takes any number of arguments. The arguments are evaluated left to right. If all arguments evaluate to non-nil, then the value of the last argument is returned. Otherwise nil is returned. | (and A B) will return NIL. |
| or | It takes any number of arguments. The arguments are evaluated left to right until one evaluates to non-nil, in such case the argument value is returned, otherwise it returns nil. | (or A B) will return 5. |
| not | It takes one argument and returns t if the argument evaluates to nil. | (not A) will return T. |