Библиотека сайта rus-linux.net
Learning OCaml, for C, C++, Perl and Java programmers by Richard W.M. Jones
Глава 1.
Перевод: И.Зенков
Содержание
КомментарииКомментарии в OCaml разделяются (* This is a single-line comment. *) (* This is a * multi-line * comment. *)
Другими словами договоренность о комментариях, очень схожа с
оригинальным C ( На данный момент, правда, не
существует отдельной спецификации для однострочных комментариев
(как OCaml поддерживает вложенные блоки
(* This code is broken ... (* Primality test. *) let is_prime n = (* note to self: ask about this on the mailing lists *) XXX;; *) Вызов функцииПредположим, что вы создали
функцию - мы назовём её В большинстве C-производных языках, вызов функции выглядит так: repeated ("hello", 3) /* this is C code */
Это означает: "вызвать функцию OCaml будучи родственным c другими функциональными языками, отличается написанием и заключением в кавычки вызова функции и именно от сюда происходит множество ошибок. Вот пример вызова функции: repeated "hello" 3 (* this is OCaml code *) Обратите внимание - НЕТ никаких скобок и НЕТ никаких запятых между аргументами. Но не удивляетесь, что Теперь представим, что у нас есть
функция - /* C code: */ repeated (get_string_from_user ("Please type in a string."), 3) (* OCaml code: *) repeated (get_string_from_user "Please type in a string.") 3 Обязательно обратите внимание на расположение скобок и отсутствие запятых. Отсюда следует основное правило: "скобками отделяется передаваемая функция, при этом не нужно помещать запятые между аргументами, передаваемыми вызываемой функции". Далее ещё несколько примеров: f 5 (g "hello") 3 (* f has three arguments, g has one argument *) f (g 3 4) (* f has one argument, g has two arguments *) # repeated ("hello", 3);; (* OCaml will spot the mistake *) This expression has type string * int but is here used with type string Определение функцииВы все должно быть знаете как определяются функции (или "static method" у кофеманов) в распространённых языках. Как же это делается в OCaml? Синтаксис OCaml приятно краток. Ниже определяется функция которая берёт два числа с плавающей точкой и возвращает среднее число. let average a b = (a +. b) /. 2.0;;
Наберите это в OCaml "toplevel" (в Unix, "toplevel"
вызывается командой # let average a b = (a +. b) /. 2.0;; val average : float -> float -> float = <fun> Взглянув на завершение определения функции, и возвращённый OCaml'ом результат (помечен курсивом), у вас возможно появятся следующие вопросы:
Ответы на эти вопросы можно найти
в следующем разделе, но в начале я хотел бы определить туже самую
функцию на C (Java определение, в принципе достаточно схоже), и
возможно нам удастся разрешить ещё больше вопросов. Далее наша C
версия float average (float a, float b) { return (a + b) / 2; } Как мы видем, наше OCaml определение гораздо короче. Возможно вы спросите:
Хорошо, потому немного ответов.
Более детально, всё рассмотрено в следующем разделе. Основные типыВ OCaml существуют следующие основные типы:
Для OCaml не имеет основного типа для
целого числа без знака, но вы можете добиться того же эффекта
используя OCaml предоставляет тип Строки это не просто списки символов. Они имеют своё собственное более эффективное внутренние представление. Тип Неявное преобразование типа против явногоВ C-исходных языках, целые числа
вычисляются преобразуясь в числа с плавающей точкой, повышенной
точности. Например написав OCaml никогда не производит
неявного преобразования, потому вызов # 1 + 2.5;; This expression has type float but is here used with type int
В "переводе с Французского" языка OCaml, сообщение об
ошибке можно растолковать как: "вы передали
Для сложения двух чисел с
плавающей точкой, необходимо использовать другой оператор, И в данном случае OCaml не
переводит автоматически # 1 +. 2.5;; This expression has type int but is here used with type float В этом случае OCaml жалуется на первый аргумент. Хорошо, но, что если нам
действительно необходимо сложить вместе целое и число с плавающей
точкой? Скажем хранящиеся в переменных (float_of_int i) +. f;;
Какое преобразование типов лучше?Вам наверное показалось будь-то явное преобразование ужасная операция требующая больших затрат времени и вы конечно имеете на это право, но есть по крайней мере два аргумента в пользу подобного подхода. Во-первых, явное преобразование позволяет реализовать типовой интерфейс (см. ниже), а он в свою очередь прекрасно экономит время компенсируя преобразование типов методом печати. Во-вторых, если вы когда-нибудь проводили отладку программ на C, вы должны знать, что (a) из-за неявного преобразования иногда происходят ошибки, которые очень трудно найти и (б) большенство времени своей работы вы тратите на то, чтобы найти где-же произошло это неявное преобразование. Явные преобразования делают отладку намного более простым занятием. Обычные и рекурсивные функцииВ отличии от C-исходных языков, в
OCaml функция становится рекурсивной только после явного указания
с использованием let rec range a b = if a > b then [] else a :: range (a+1) b ;;
Обратите внимание, Всё различие между Типы функцийВозможно с типами вы будите
работать нечасто, но если понадобиться то описывать вы будите типы
для ваших функций. Однако, OCaml множество раз печатает свои
сообщения о типах функций и потому было необходимо хорошо
разбираться в синтаксисе этих самых сообщений. Например для
функции f : Сейчас стрелочки кажутся несколько странными, но когда мы дойдём до так называемых "currying", вы увидите почему был выбран такой синтаксис. Но для начала я просто покажу вам несколько примеров. Функция repeated : string -> int -> string
Функция average : float -> float -> float
Стандартная преобразующая функция int_of_char : char -> int
Если функция ничего не возвращает ( output_char : out_channel -> char -> unit Полиморфные функцииТеперь немного странного. Как на счёт функции получающей что-нибудь в качестве аргумента? Вот некая функция которая берёт один аргумент, но игнорирует его всё время возвращая 3: let give_me_a_three x = 3;; Какого она типа? В OCaml мы используем специальный определитель подразумевая "любой тип, какой вы только захотите". Это один знак апострофа перед буквой. Тип предыдущей функции может быть описан как: give_me_a_three : 'a -> int
Где Нет необходимости объяснять почему используют полиморфные функции, но то, что их используют и то, что они очень важны это факт, потому позже мы вернёмся к этой теме. Маленькая подсказка: полиморфизм похож на "templates" в C++ или "generics" в Java 1.5. Выбор типаСуть данного руководства в том, что функциональные языки обладают Действительно Замечательными Особенностями и, что OCaml объединил все эти Действительно Замечательные Особенности в одном, став очень практичным языком для использования настоящими программистами. Но странная вещь, что большенство всех этих замечательные особенности не имеет никакого отношения к функциональному программированию, вообще. Фактически только сейчас, я расскажу о первой Действительно Замечательной Особенности, умолчав почему функциональное программирование называется "функциональным". Так или иначе, первая Действительно Замечательная Особенность: выбор типа. Проще говоря: вам не нужно объявлять типы функций и переменных, OCaml сделает эту работу за вас. Кроме того OCaml продолжает проверять все ваши типы, сравнивая (даже параллельно между различными файлами). Но OCaml -- также практический язык, и по этой причине он обладает обходными путями в системе типов, для пропуска этих самых проверок. Правда пользуются этим редко и только гуру могут захотеть обойти контроль соответствия типов. Давайте теперь вернёмся к функции
# let average a b = (a +. b) /. 2.0;; val average : float -> float -> float = <fun>
Свершилось чудо! OCaml сам во всём разобрался, поняв что функция
берёт два аргумента типа Как ему это удалось? Во-первых он
проверяет где используются Во-вторых, функция average : float -> float -> float
Выбор типа для такой маленькой программы разумеется очень прост,
но он работает и в больших проектах, помогая сократить время и
избежать таких ошибок как |