3.3. Списки, списки и ещё раз списки

Теперь, когда вы знаете о переменных и функциях, обратимся к спискам в Scheme.

3.3.1. Определение списка

До разговора о списках необходимо упомянуть разницу между неделимыми значениями и списками.

В предыдущем уроке вы уже видели неделимые значения, которые мы присвоили переменным. Неделимое значение — это одно-единственное значение. Так, мы можем присвоить переменной «x» значение 8 следующим утверждением:

(let* ( (x 8) ) x)

Мы добавили выражение x в конце, чтобы вывести значение переменной x. Обычно этого не требуется. Заметьте, что оператор let* ведёт себя как функция: возвращается значение последнего выражения.

Переменная также может содержать спискок значений вместо одного значения. Чтобы присвоить переменной x список значений 1, 3, 5, нужно ввести:

(let* ( (x '(1 3 5))) x)

Попробуйте ввести оба утверждения в консоль Script-Fu и посмотрите, что они выводят.

8

При вводе второго утверждения, выводится следующее:

(1 3 5)

Когда вывелось значение 8, это значит, что переменная x содержит неделимое значение 8. Однако, когда вывелось (1 3 5), это значит, что переменная x содержит не одно значение, а список значений. Заметьте, что ни в декларации или присвоении, ни в выведенном результате нет запятых.

Синтаксис для создания списка следующий:

'(a b c)

Где a, b и c — одиночные значения. Мы используем апостроф (') чтобы указать, что за ним в скобках следует список одиночных значений, а не функция или выражение.

Пустой список создаётся так:

'()

или так:

()

Списки могут содержать как неделимые значения, так и другие листы:

(let*
   (
        (x
           '("GIMP" (1 2 3) ("is" ("great" () ) ) )
        )
    )
    x
)
      

Заметьте, что после первого апострофа не надо больше ставить апостроф перед внутренними списками. Введите это выражение и посмотрите, что получится.

Вы заметите, что возвращённый результат — не список одиночных неделимых значений, а список, содержащий буквальное значение (The GIMP), список (1 2 3) и т.д.

3.3.2. Как думать о списках

Полезно думать о списках, как состоящих из «головы» и «хвоста». Голова — первый элемент списка, хвост — остаток списка. Вы увидите, почему это важно, как речь пойдёт о сложении списков и о доступе к элементам списка.

3.3.3. Создание списков сцеплением (Функция Cons)

Наиболее употребляемая функция это функция cons. Она берёт любое значение и добавляет его ко второму параметру, списку. Если использовать терминологию вышесказанного, это функция добавляет значение в голове списка. Поэтому список можно создать следующим образом:

(cons 1 '(2 3 4) )

В результате получается список (1 2 3 4).

Можно также создать список с одним элементом:

(cons 1 () )

Можно использовать ранее декларированные переменные вместо буквальных значений.

3.3.4. Создание списка с помощью функции list

Создать список из буквальных значений и переменных можно с помощью функции list:

(list 5 4 3 a b c)

Это создаст и вернёт список, содержащий значения переменных a, b и c. На пример:

        (let*  (
                  (a 1)
                  (b 2)
                  (c 3)
               )

               (list 5 4 3 a b c)
        )
      

Этот сценарий создаёт список (5 4 3 1 2 3).

3.3.5. Доступ к элементам списка

Чтобы достать значения из списка, используйте функции car и cdr, которые возвращают первый элемент списка и остаток списка, соответственно. Назначение этих функций совпадает с терминологией голова-хвост, описанной выше.

3.3.6. Функция car

car возвращает первый элемент списка (голова списка). Список не может быть пустой. Следующее выражение вернёт первое значение:

(car '("первый" 2 "третий"))

то есть

"первый"

3.3.7. Функция cdr

cdr возвращает остаток списка после первого элемента (хвост списка). Если в списке только один элемент, то возвращается пустой список.

(cdr '("первый" 2 "третий"))

возвращает

(2 "третий")

тогда как

(cdr '("один единственный"))

возвращает

()

3.3.8. Доступ к другим элементам списка

Теперь, когда мы можем достать первый элемент списка и всё остальное, поговорим о том, как достать другие элементы (второй, третий, …) списка. Существует несколько вспомогательных функций, позволяющие достать, например, голову хвоста списка (caadr) или хвост хвоста списка (cddr), и т.д.

Номенклатура названий проста: a означает голова, d означает хвоста, поэтому

(car (cdr (car x) ) )

можно написать как:

(cadar x)

Для практики по доступу к спискам, попробуйте ввести следующее (всё на одной линии, если используете консоль) и, используя функции car и cdr, достать разные элементы из списка:

        (let* (
                 (x  '( (1 2 (3 4 5) 6)  7  8  (9 10) )
                 )
              )
              ; поместите своё выражение с car/cdr здесь
        )
      

Попробуйте достать число 3 из списка при помощи только двух вызовов функций. Если сможете, то вы на правильном пути стать мастером Script-Fu!

[Замечание] Замечание

В Scheme точка с запятой (;) указывает на комментарий. Всё, что следует за ней, игнорируется интерпретатором сценариев. Поэтому, используйте комментарии для пометок и описания сложных выражений, чтобы не забыть, что они делают.