Введение в язык Scheme для школьников: различия между версиями

Материал из Викиучебника — открытых книг для открытого мира
Содержимое удалено Содержимое добавлено
Нет описания правки
Нет описания правки
Строка 147: Строка 147:
[[Категория:Программирование]]
[[Категория:Программирование]]
[[Категория:Функциональное программирование]]
[[Категория:Функциональное программирование]]
[[Категория:Языки программирования]]

Версия от 06:09, 6 июня 2006

Знакомство с грамматикой

Вы хорошо знаете русский, возможно, неплохо уже говорите по-английски, в школе вас научили несколько необычному языку математики. Предлагаю вам выучить ещё один — LISP. Точнее, один из его самых интересных диалектов — Scheme (правильно произносится название как «ским», однако можно говорить и проще — «схема») Начнём с того, что познакомимся с несколько необычным порядком слов этого языка: «действие объект объект ...». Хотя почему необычным? Вот несколько фраз на русском языке, организованных по тому же принципу: «Сумма трёх и пяти» «Произведение пяти, шести и семи» «Купи в магазине хлеб» Каждая законченная фраза на этом языке должна быть окружена парой круглых скобок. Запишем сказанное выше на Scheme:

(+ 3 5)
(* 5 6 7)
(buy shop bread)

Можно записать выражения и посложнее:

(+ 2 3 (+ 5 6) ) - «Сумма двух, трёх и суммы пяти и шести.»

Просто, не правда ли? Давайте двигаться дальше. Фраза «(* 3 5) — хороша, а «(* width height)» лучше. Выражение «(* 2 3.1415926 5)» — интригующе, а «(* 2 pi radius)» гораздо более осмысленно. Здесь width, height — переменные, а 3 и 5 — их текущие значения. Переменная задаётся следующей конструкцией языка:

(define имя «первоначальное значение»)

Пример:

(define width 3)
(define height 7)
(* 2 (+ width height))

Прочитаем записанное по-русски: «Положим ширина — это 3, высота — это 7, подсчитаем произведение двух и суммы ширины и высоты (например, периметр прямоугольника)». Результат такого вычисления в нашем случае будет 20. Продолжим совершенствовать конструкции. Допустим, нам требуется подсчитать сумму квадратов двух чисел. Это можно сделать например так:

(define a 3)
(define b 4)
(+ (* a a) (* b b))

Не правда ли, что-то режет глаз? Cлишком громоздко — так мы никогда не говорим. Если бы у нас был в языке глагол который, означал бы «квадрат числа», то последнее выражение звучало бы:

(+ (square a) (square b))

Согласитесь, что так гораздо лучше. Есть задача — есть её решение. Мы можем объявить новое слово-функцию, назвать её square. Функция будет принимать в качестве параметра число и возвращать его квадрат. Делается это следующим образом:

(define (square x) (* x x))

Общий формат:

(define (название параметр параметр...) тело функции)

Функция возвращает последнее вычисленное значение. Это означает, что следующая функция square2:

(define (square2 x) (+ x x) (* x x))

вернёт тот же результат, что и square, только попутно сделает ещё одно ненужное дело — удвоение числа. Перепишем пример с суммой квадратов чисел заново:

(define a 3)
(define b 4)
(define (square x) (* x x))
(+ (square a) (square b))

Нам не хватало слов в языке — мы их добавили. Вообще, когда вы пишете программу на LISP, вы описываете не алгоритм, а сначала создаёте язык, а потом на нём формулируете исходную задачу. Несколько точнее — вы «подгоняете» данный вам Scheme язык до тех пор, пока он не станет совпадать с языком, на котором задача формулируется легко. Сразу пример. Пусть перед нами стоит задача сделать программу, которая спрашивает имя пользователя, а потом выводит ему приветствие. Scheme предоставляет нам несколько готовых «глаголов»:

  • read — для чтения имени
  • display — для того чтобы рисовать что-то на экране
  • newline — для того чтобы рисовать на экране «перевод строки»

Мы бы хотели иметь такие «глаголы»:

  • hello — для приветствия с одним параметром — именем пользователя
  • username — для получения имени пользователя, без параметров.

На таком языке наша задача выглядела бы следующим образом:

(hello (username))

Дело за малым — определить hello и username. Нет проблем. Вот полный текст программы.

(define (hello name)
  (display "Hello ")
  (display name)
  (display "!")
  (newline))
(define (username)
  (write "Enter your name:")
  (read))
(hello (username))

LISP — полноценный функциональный язык, а поэтому функции — полноправные члены этого языка, независимо от того, определили вы их сами, или они уже были в языке «готовые». В частности, их можно передавать в качестве параметров в другие функции, а там уже делать с ними всё, что потребуется. Например, функцию «модуль числа» можно определить так:

(define (abs x)
  (if (positive? x )
       x
      (- x)))

«Если аргумент положителен — возвращается x, иначе минус x».

А можно и так:

(define (abs x)
  ((if (positive? x) + -) x))

«Если аргумент положителен, то плюс, иначе минус x». Здесь в результате исполнения выражения if возвращается функция + или -, которая затем применяется к аргументу x. Кстати, обратите внимание на новую конструкцию if — полагаю, что её назначение вам сразу ясно. Сначала проверяется первый аргумент, если он истинен, то исполняется второй аргумент, иначе третий. Общий формат следующий:

(if условие «действие, если условие выполняется» «действие в противном случае»)

Где посмотреть и попробовать

В теории всё хорошо, а где немного попрактиковаться? В мире можно найти много прекрасно разработанных сред для работы со Scheme. К сожалению, большинство документации по Scheme на английском языке, но можно найти и отличные введения на русском — язык-то простой.

Вот названия нескольких самых распространённых реализаций:

Plt Scheme — одна из самых полных реализаций, включает в себя удобную обучающую среду Dr.Scheme. Есть версии для платформ Windows, Linux, Mac OS.

Bigloo — тоже достаточно полная реализация. Доступна для платформ Windows, Linux.

LispMe — версия для карманных компьютеров с операционной системой Palm OS.

Также ещё посмотрите Gambit-C — один из самых быстрых компиляторов Scheme.

Все перечисленные реализации Scheme — это интерпретаторы. Запускаете интерпретатор — и можно вести с ним диалог на Scheme: в ответ на его приглашение вводите конструкции на Scheme, а он будет возвращать результаты вычислений.

Wecome to Mz Scheme version 208, Copyright (c) 2004  PLT Scheme, Inc.
>1
1
>(+ 1 2)
3
>(define a 3)
>(+ a a)
6
>

Попробуйте «проиграть» все вышеперечисленные примеры. Думаю, вам понравится!

Кто в мешке

Простота Scheme обманчива. На самом деле — это один из самых мощных на сегодняшний день языков программирования. На основе этого языка можно изучить все известные стили и методы программирования. С частью этих приёмов мы познакомимся с вами в следующих статьях этой серии.

Упражнение.

Посмотрите следующие две реализации функции вычисления факториала . Одна из них основана на рекурсии, а другая – на итерациях. Напишите на языке Scheme рекурсивную и основанную на итерациях реализации функции возведения в степень .

Вариант 1

(define (factorial n)
  (if (= n 0)
      1
      (* n (factorial (- n 1)))))

Вариант 2

(define (fact-iter result counter)
 (if (= counter 0)
     result
     (fact-iter (* counter result)
                (- counter 1))))
(define (factorial n) (fact-iter 1 n))

См. также

  • LISP, статья в Википедии
  • Scheme, статья в Википедии
  • Plt Scheme, официальная страница Plt Scheme
  • Bigloo, страничка о Bigloo
  • LispMe, официальная страница LispMe
  • Gambit-C, здесь можно скачать Gambit-C

Учебники на английском