СЕМИНАР40сентябрь 2011 / ИНФОРМАТИКАмного инфиксных чисел, и большая их часть пришлаиз мира математики. Основная масса функцийявляются префиксными, то есть их имена записываютсяперед аргументами, например, max 1 3.Заметьте особое отличие вызова функций в Haskellот вызовов в других языках — аргументы при записивызова пишутся без скобок и каких-либо разделительныхзнаков, поскольку все это имеет несколькодругой смысл.Разумеется, вы запросто можете определить собственныефункции. Вы должны разместить описаниефункции в файле с расширением *.hs и загрузитьэтот файл командой :l — определение функцийв командной строке интерпретатора работатьне будет (в дальнейшем будет описан способ сделатьэто, если уж очень хочется)! Опишем, к примеру,вышеупомянутую функцию doubleMe, котораяудваивает собственный аргумент. Это просто:doubleMe x = x + xили:doubleMe x = x * 2оба определения математически правильны и эквивалентны.Разумеется, вы можете определить другиефункции, используя уже определенные вамифункции (причем сделать это можно даже до определенияпоследних!):doubleMeTwice x = doubleMe (doubleMe x)Также вы можете использовать условные выраженияпри описании функций. Например, нам надоопределить функцию, которая будет удваиватьлишь числа не бÓльшие ста:doubleFromFirstHundred x = if x > 100then x else doubleMe xЗдесь необходимо заметить, что ветвь else вязыке Haskell обязательна по следующей причине:условный оператор является выражением, котороевсегда должно иметь определенное значение. Онни в коем случае не является чистой функцией, посколькузависит от значения переменных, определенныхвовне его, но он является полноправнымматематическим выражением.Константные выражения в Haskell тоже являютсяфункциями: функциями, не имеющими аргументови, следовательно (вспомните правило: чистаяфункция всегда должна давать один и тот же результатдля одних и тех же входных данных), имеющимиединственно возможное значение:temperature = 35Напоследок для людей, заинтересовавшихсяправилами именования функций: имя функциив Haskell не может начинаться с заглавной буквы,поэтому имена всех вышерассмотренных функцийначинаются со строчных букв. Кроме того, принятоеще одно интересное соглашение о наименованиифункций: имя функции, заканчивающееся на апостроф,обозначает либо неленивую функцию, либонесколько измененный вариант уже имеющейсяфункции. Такое именование — стандартная практикапри программировании на языке Haskell.Списки, списки, спискиСписок — одна из основных структур данных вязыке Haskell. Если вы имеете хоть малейший опытпрактического программирования, вы уже, вне сомнения,знаете, что такое список. В Haskell спискомможет быть однородное множество элементов. Выможете иметь список чисел или список символов,но не список, содержащий одновременно и числа исимволы. Например:ghci> let someList = [1, 2, 3, 4]ghci> let anotherList = ['h', 'e', 'l','l', 'o'][Замечание для любознательных читателей:слово let и есть та самая конструкция, котораяпозволяет определять функции прямо в строкеинтерпретатора (а ведь это определение спискаи есть определение функции, как я уже писалвыше). К сожалению, в Hugs это так просто несработает.]Строки являются частным случаем списков —это списки символов. Однако Haskell разрешает неписать каждый раз ['h', 'e', 'l', 'l', 'o'],когда мы хотим определить строку 'hello', вы можетезаписать ее напрямую.Одна из основных и простейших операцийнад списками — конкатенация, или склейка,списков:Hugs> [1, 2, 3, 4] ++ [9, 10, 11, 12][1, 2, 3, 4, 9, 10, 11, 12]Hugs> 'hello' ++ ' ' ++ 'world''hello world'Однако с частой конкатенацией очень длинныхсписков следует быть осторожным, поскольку этодовольно ресурсоемкая операция: при склейке двухсписков интерпретатор вынужден пройти первыйиз них от начала до конца, а, как нам известно, времяэтого прохода линейно зависит от числа элементовв списке.Теоретическая база списков в Haskell унаследованаиз языков семейства Лисп и заслуживает некоторогорассмотрения. Допустим, вы хотите добавитьэлемент в начало списка — это делается операторомдвоеточие:Hugs> 4:[1, 2, 3][4, 1, 2, 3]В данном случае число 4 называется головой списка,а список [1, 2, 3] — хвостом. Рассмотримсам список [1, 2, 3], его голова — 1, хвост —[2, 3]. Рассмотрим этот хвост, его голова — 2,хвост — [3]. Выражение [3] ни в коем случаене является числом, это список, имеющий голову3 и хвост [], где [] является специальным видомсписка — пустым списком. Таким образом,список [4, 1, 2, 3] имеет альтернативную(имеющую теоретически верный вид) форму4:1:2:3:[], однако при записи программ предпочтительноиспользовать первую форму записисписков для повышения читабельности.
При записи списков чисел (или других объектовперечислимого типа; о классах типов — дальше)можно использовать промежутки:Hugs> [1 .. 4][1, 2, 3, 4]Если правый конец промежутка не указан, томы получим бесконечный список, начинающийсяс заданного элемента (не стоит пытаться еговывести, он все-таки бесконечный), ленивостьHaskell легко позволяет работать с такими структурамиданных.Элементами списков также могут быть списки,причем не обязательно, чтобы все внутренние спискиодного большого списка имели одинаковуюдлину: в Haskell тип список обозначает список любойдлины.Операций над списками в Haskell великое множество:это и традиционная для массивов индексация,и взятие головы и хвоста списка, и подсчетдлины, и нахождение минимального и максимальногоэлементов, и многое-многое другое. За полнымперечнем можно обратиться к специальнойлитературе [4, 5, 6]. Для нас сейчас самыми интереснымиявляются свертки списка.Суммирование элементов списка:Hugs> sum [1, 2, 3, 4]10Подсчет произведения элементов списка:Hugs> product [1, 2, 3, 4]24С помощью таких сверточных функций красивореализуются многие математические задачи, кпримеру, факториал:factorial n = product [1 .. n]Посмотрите на это определение функции,считаю щей факториал, и скажите, что оно вамнапоминает? Да это же один в один математическоеопределение: “Факториал числа n равен произведениювсех чисел от 1 до n включительно”!Вспомните еще хоть один неспециализированныйязык, который позволил бы вам определятьфункции в том виде, в котором они задаются вматематике.Одним из самых мощных средств работы сосписками в Haskell являются так называемые списочныевыражения. Вспомните, как определяютсямножества в математике, например: “МножествоS — это множество x, принадлежащих множествуN и удовлетворяющих системе неравенств...”.В языке Haskell имеются все средства для заданиямножеств точно таким же языком! Например, множествоудвоенных чисел из промежутка от 1 до 10,тогда вы можете записать:Hugs> [x * 2 | x