Scheme/Tutorial/3
Материал из ALT Linux Wiki
м (вычитка) |
|||
(6 промежуточных версий не показаны.) | |||
Строка 1: | Строка 1: | ||
- | Третья часть рассказа. Наверное самая сложная, но если что-то будет | + | Третья часть рассказа. Наверное, самая сложная, но если что-то будет неясно — ничего страшного, в следующих частях всё постепенно прояснится. |
==== 6 Несколько замечаний про функции ==== | ==== 6 Несколько замечаний про функции ==== | ||
- | В прошлый раз было подробно рассказано как описываются функции. Несколько полезных замечаний: | + | В прошлый раз было подробно рассказано, как описываются функции. Несколько полезных замечаний: |
1. У длинного определения | 1. У длинного определения | ||
Строка 11: | Строка 11: | ||
<tt>(define (f) ... )</tt> | <tt>(define (f) ... )</tt> | ||
- | 2. Процедура является таким же полноправным типом данных как и числа и строки, поэтому их можно как передавать в качестве аргумента, так и возвращать в качестве ответа. | + | 2. Процедура является таким же полноправным типом данных, как и числа и строки, поэтому их можно как передавать в качестве аргумента, так и возвращать в качестве ответа. |
- | Вот, например, как можно было бы определить функцию | + | Вот, например, как можно было бы определить функцию «модуль числа»: |
<tt>(define (abs x) ((if (< x 0) - +) x))</tt> | <tt>(define (abs x) ((if (< x 0) - +) x))</tt> | ||
Строка 32: | Строка 32: | ||
==== 7 О символах ==== | ==== 7 О символах ==== | ||
- | Из чего сделана переменная? | + | Из чего сделана переменная? Переменная — это некоторое «имя» и «связь» между этим именем и каким-то объектом, и прежде всего именно «связь» (связи может не быть, а имена есть всегда). То есть можно рассматривать по отдельности: отдельно имя, и отдельно некоторая таблица, которая ставит соответствие между именами и объектами, на которые они ссылаются. |
<pre>(define a 4)</pre> | <pre>(define a 4)</pre> | ||
- | Это значит | + | Это значит, есть имя <tt>a</tt> и оно ссылается на объект — число <tt>4</tt>. |
- | Когда интерпретатор видит выражение <tt>(define b a)</tt>, он обнаруживает наличие имени <tt>a</tt>, а потом производит поиск в своей таблице имён, отмечает, что <tt>a</tt> ссылается на | + | Когда интерпретатор видит выражение <tt>(define b a)</tt>, он обнаруживает наличие имени <tt>a</tt>, а потом производит поиск в своей таблице имён, отмечает, что <tt>a</tt> ссылается на объект — число <tt>4</tt> и производит подстановку, после которой выражение приобретает вид <tt>(define b 4)</tt>. |
- | А что если мы хотим получить просто | + | А что если мы хотим получить просто «имя»? Тогда мы говорим интерпретатору: пожалуйста, не надо размышлять над следующим выражением, дай мне его «как есть» — эта процедура носит имя <tt>quote</tt>. |
- | <tt>(define b (quote a))</tt> | + | <tt>(define b (quote a))</tt> назначит «имени» <tt>b</tt> уже не <tt>4</tt>, а просто «имя» <tt>a</tt>. |
- | + | «имена» являются одним из типов данных схемы и называются символами. | |
- | У длинного варианта записи <tt>(quote объект)</tt> существует сокращенный: <tt>'объект</tt> | + | У длинного варианта записи <tt>(quote объект)</tt> существует сокращенный: <tt>'объект</tt> — то есть можно написать так: <tt>(define b 'a)</tt> |
- | + | ||
- | Чтобы ещё лучше прочувствовать символы, запустим интерпретатор. Мы будем вводить выражения, а он будет писать, во что они проинтерпретировались: | + | Чтобы ещё лучше прочувствовать символы, запустим интерпретатор. Мы будем вводить выражения, а он будет писать, во что они проинтерпретировались (пакет ''gambit''): |
- | <pre>$ | + | <pre>$ gsc |
- | Gambit | + | Gambit v4.5.3 |
> (define a 5) | > (define a 5) | ||
Строка 66: | Строка 65: | ||
></pre> | ></pre> | ||
- | Обратите внимание на то что переменной <tt>b</tt> нет (точнее связи нет), но | + | Обратите внимание на то, что переменной <tt>b</tt> нет (точнее, связи нет), но «имя» <tt>b</tt> без связи с чем-либо замечательно интерпретируется. |
- | Не сильно огорчайтесь, если | + | Не сильно огорчайтесь, если что-то сейчас неясно — всё прояснится, когда мы чуть глубже поймём, как работает интерпретатор. |
==== 8 Универсальный клей ==== | ==== 8 Универсальный клей ==== | ||
- | Практически нет программ, которые работали бы с базовыми типами предоставляемыми языком (строки, числа, символы) | + | Практически нет программ, которые работали бы с базовыми типами предоставляемыми языком (строки, числа, символы): каждый язык предоставляет возможность разработчику создавать свои новые типы, составленные из других типов (из базовых или других составных). |
- | Scheme предлагает только один способ сделать составной | + | Scheme предлагает только один способ сделать составной тип — объединение двух объектов в пару. |
Делается это так: | Делается это так: | ||
<pre>(cons 3 4) ; "создать пару из 3 и 4" | <pre>(cons 3 4) ; "создать пару из 3 и 4" | ||
(cons "aaa" "bbb")</pre> | (cons "aaa" "bbb")</pre> | ||
- | Доступ к первому и второму элементу пар обеспечивают функции <tt>car</tt> и <tt>cdr</tt>: // | + | Доступ к первому и второму элементу пар обеспечивают функции <tt>car</tt> и <tt>cdr</tt>: // CAR (Contents of Address Register), CDR (Contents of Decrement Register). На этих регистрах вычислительной машины IBM 605 автор Лиспа Джон Маккарти хранил голову и хвост списка в первой реализации Лисп-системы. |
<pre>(define a (cons 'first 'second)) | <pre>(define a (cons 'first 'second)) | ||
(car a) ; получить первый элемент пары, то есть "имя" "first" | (car a) ; получить первый элемент пары, то есть "имя" "first" | ||
Строка 84: | Строка 83: | ||
Пары вполне достаточно, чтобы создать такую известную структуру как односвязный список. | Пары вполне достаточно, чтобы создать такую известную структуру как односвязный список. | ||
- | Для этого первый элемент пары будет ссылаться на содержимое элемента списка, а | + | Для этого первый элемент пары будет ссылаться на содержимое элемента списка, а второй — на следующий элемент списка. Последний элемент списка должен ссылаться на «никого нет», мы для этого введём специальное имя <tt>()</tt>. |
- | Вот например так создаётся список из двух элементов, содержащих <tt>3</tt> и <tt>4</tt>: | + | Вот, например, так создаётся список из двух элементов, содержащих <tt>3</tt> и <tt>4</tt>: |
<pre>(define elem2 (cons 4 '() )) ; второй элемент содержит 4 и ссылается на "никого больше нет" | <pre>(define elem2 (cons 4 '() )) ; второй элемент содержит 4 и ссылается на "никого больше нет" | ||
(define elem1 (cons 3 elem2)) ; первый элемент содержит 3 и ссылается на второй</pre> | (define elem1 (cons 3 elem2)) ; первый элемент содержит 3 и ссылается на второй</pre> | ||
- | В | + | В «развернутом виде» это выглядит так: |
<tt>(define elem1 (cons 3 (cons 4 '())))</tt> | <tt>(define elem1 (cons 3 (cons 4 '())))</tt> | ||
Строка 96: | Строка 95: | ||
<tt>(cons 1 (cons 2 (cons 3 '())))</tt> | <tt>(cons 1 (cons 2 (cons 3 '())))</tt> | ||
- | Как видно конструкция очень громоздкая, поэтому есть более короткий вариант: | + | Как видно, конструкция очень громоздкая, поэтому есть более короткий вариант: |
<tt>(list 1 2 3)</tt> | <tt>(list 1 2 3)</tt> | ||
Строка 105: | Строка 104: | ||
(cdr a) ; получить "хвост", то есть ссылку на второй элемент | (cdr a) ; получить "хвост", то есть ссылку на второй элемент | ||
(car (cdr a)) ; получить содержимое второго элемента | (car (cdr a)) ; получить содержимое второго элемента | ||
- | (cdr (cdr a)) ; получить "хвост" после второго элемента, то есть третий элемент</pre> | + | (cdr (cdr a)) ; получить "хвост" после второго элемента, то есть ссылку на третий элемент |
+ | (car (cdr (cdr a))) ; получить третий элемент</pre> | ||
Опять-таки для веера из <tt>car</tt> и <tt>cdr</tt> существует сокращенные варианты записи: | Опять-таки для веера из <tt>car</tt> и <tt>cdr</tt> существует сокращенные варианты записи: | ||
- | вместо (car (car .. | + | вместо (car (car .. — (caar .. |
- | вместо (car (cdr .. | + | вместо (car (cdr .. — (cadr .. |
- | вместо (car (cdr (cdr | + | вместо (car (cdr (cdr … — (caddr .. |
- | вместо (car (car (cdr | + | вместо (car (car (cdr … — (caadr .. |
- | вместо (car (cdr (cdr (cdr .. | + | вместо (car (cdr (cdr (cdr .. — (cadddr .. |
- | Надеюсь | + | Надеюсь, уловили закономерность? Впрочем, пользоваться этим, скорее всего, не придётся. В нашем примере: |
<pre>(cadr a) ; получить содержимое второго элемента | <pre>(cadr a) ; получить содержимое второго элемента | ||
(caddr a) ; получить содержимое третьего элемента | (caddr a) ; получить содержимое третьего элемента | ||
- | (cdddr a) ; получить "хвост" третьего элемента, то есть "никого нет" или символ "()".</pre> | + | (cdddr a) ; получить "хвост" третьего элемента, то есть ссылку на |
+ | "никого нет" или символ "()".</pre> | ||
+ | |||
+ | '''[[Scheme/Tutorial/4|далее>>]]''' | ||
+ | |||
+ | {{Category navigation|title=Scheme|category=Scheme|sortkey=Tutorial}} |
Текущая версия на 08:33, 11 мая 2012
Третья часть рассказа. Наверное, самая сложная, но если что-то будет неясно — ничего страшного, в следующих частях всё постепенно прояснится.
6 Несколько замечаний про функции
В прошлый раз было подробно рассказано, как описываются функции. Несколько полезных замечаний:
1. У длинного определения (define f (lambda (x y) ... )) есть более короткая форма (define (f x y) ... ) если процедура без аргументов, то определение в упрощённой форме будет выглядеть как: (define (f) ... )
2. Процедура является таким же полноправным типом данных, как и числа и строки, поэтому их можно как передавать в качестве аргумента, так и возвращать в качестве ответа. Вот, например, как можно было бы определить функцию «модуль числа»: (define (abs x) ((if (< x 0) - +) x))
3. Дополнение к предыдущему. Если вас не смущает сведение выражения:
(define a (+ 1 2)) (* a 5)
к:
(* (+ 1 2) 5)
то не должно смущать и сведение:
(define f (lambda (x) (+ x x)) (g f)
к:
(g (lambda (x) (+ x x)))
То есть когда вы передаёте функцию в качестве аргумента, то можно сначала сконструировать, дать ей имя и передать указывая имя, а можно и не давать имени, а сразу сконструировать и передать.
7 О символах
Из чего сделана переменная? Переменная — это некоторое «имя» и «связь» между этим именем и каким-то объектом, и прежде всего именно «связь» (связи может не быть, а имена есть всегда). То есть можно рассматривать по отдельности: отдельно имя, и отдельно некоторая таблица, которая ставит соответствие между именами и объектами, на которые они ссылаются.
(define a 4)
Это значит, есть имя a и оно ссылается на объект — число 4.
Когда интерпретатор видит выражение (define b a), он обнаруживает наличие имени a, а потом производит поиск в своей таблице имён, отмечает, что a ссылается на объект — число 4 и производит подстановку, после которой выражение приобретает вид (define b 4).
А что если мы хотим получить просто «имя»? Тогда мы говорим интерпретатору: пожалуйста, не надо размышлять над следующим выражением, дай мне его «как есть» — эта процедура носит имя quote. (define b (quote a)) назначит «имени» b уже не 4, а просто «имя» a.
«имена» являются одним из типов данных схемы и называются символами. У длинного варианта записи (quote объект) существует сокращенный: 'объект — то есть можно написать так: (define b 'a)
Чтобы ещё лучше прочувствовать символы, запустим интерпретатор. Мы будем вводить выражения, а он будет писать, во что они проинтерпретировались (пакет gambit):
$ gsc Gambit v4.5.3 > (define a 5) > 5 5 > a 5 > 'a a > > b *** ERROR IN (console)@5.1 -- Unbound variable: b 1> > 'b b >
Обратите внимание на то, что переменной b нет (точнее, связи нет), но «имя» b без связи с чем-либо замечательно интерпретируется.
Не сильно огорчайтесь, если что-то сейчас неясно — всё прояснится, когда мы чуть глубже поймём, как работает интерпретатор.
8 Универсальный клей
Практически нет программ, которые работали бы с базовыми типами предоставляемыми языком (строки, числа, символы): каждый язык предоставляет возможность разработчику создавать свои новые типы, составленные из других типов (из базовых или других составных).
Scheme предлагает только один способ сделать составной тип — объединение двух объектов в пару. Делается это так:
(cons 3 4) ; "создать пару из 3 и 4" (cons "aaa" "bbb")
Доступ к первому и второму элементу пар обеспечивают функции car и cdr: // CAR (Contents of Address Register), CDR (Contents of Decrement Register). На этих регистрах вычислительной машины IBM 605 автор Лиспа Джон Маккарти хранил голову и хвост списка в первой реализации Лисп-системы.
(define a (cons 'first 'second)) (car a) ; получить первый элемент пары, то есть "имя" "first" (cdr a) ; получить второй элемент пары, то есть "имя" "second"
Пары вполне достаточно, чтобы создать такую известную структуру как односвязный список. Для этого первый элемент пары будет ссылаться на содержимое элемента списка, а второй — на следующий элемент списка. Последний элемент списка должен ссылаться на «никого нет», мы для этого введём специальное имя ().
Вот, например, так создаётся список из двух элементов, содержащих 3 и 4:
(define elem2 (cons 4 '() )) ; второй элемент содержит 4 и ссылается на "никого больше нет" (define elem1 (cons 3 elem2)) ; первый элемент содержит 3 и ссылается на второй
В «развернутом виде» это выглядит так: (define elem1 (cons 3 (cons 4 '())))
Если захотим сделать список из трёх элементов 1, 2 и 3, то надо написать: (cons 1 (cons 2 (cons 3 '())))
Как видно, конструкция очень громоздкая, поэтому есть более короткий вариант: (list 1 2 3)
Поскольку список склеен из пар, то и работать с ним можно при помощи при помощи тех же car и cdr. (define a (list 1 2 3))
(car a) ; получить содержимое первого элемента (cdr a) ; получить "хвост", то есть ссылку на второй элемент (car (cdr a)) ; получить содержимое второго элемента (cdr (cdr a)) ; получить "хвост" после второго элемента, то есть ссылку на третий элемент (car (cdr (cdr a))) ; получить третий элемент
Опять-таки для веера из car и cdr существует сокращенные варианты записи:
вместо (car (car .. — (caar .. вместо (car (cdr .. — (cadr .. вместо (car (cdr (cdr … — (caddr .. вместо (car (car (cdr … — (caadr .. вместо (car (cdr (cdr (cdr .. — (cadddr ..
Надеюсь, уловили закономерность? Впрочем, пользоваться этим, скорее всего, не придётся. В нашем примере:
(cadr a) ; получить содержимое второго элемента (caddr a) ; получить содержимое третьего элемента (cdddr a) ; получить "хвост" третьего элемента, то есть ссылку на "никого нет" или символ "()".