Решил покурить OCaml. Первой (на самом деле нет) прогой на нём стал алгоритм Дейкстры преобразования выражений в польскую нотацию. Приводить алгоритм я здесь не буду, а вот ощущениями от языка поделюсь.
Итак, для начала сама прога лежит здесь в папке ocaml_my_example. Писалась она специально в функциональном стиле, поэтому стек здесь реализован через список, а не взят из стандартной библиотеки. Для проверки результата использовалась программа на C++, лежащая на википедии.
Исполнение программы начинается со строк
которые считывают строку, преобразуют её в список токенов и выводят результат работы алгоритма. Слово in позволяет ограничивать область видимости имени только для следующих объявлений, что весьма удобно.
Перед запуском алгоритма необходимо преобразовать строку в токены. Я сделал довольно примитивную функцию (делать сложную смысла нет), которая выглядит примерно так:
В ocaml часто вместо стандартной условной конструкции удобно использовать pattern matching, что видно из примера. В нём строка str проверяется на соответствии одному из шаблонов и в зависимости от этого создаётся новый объект. Шаблон _ означает любое значение (привет, prolog).
А теперь на радость плюсистам: объекты! Да, они здесь есть, есть член-функции и есть наследование. В общем сколько-нибудь инкапсуляцию и полиморфизм обеспечить возможно. Собсна, вот как это выглядит:
Базовый класс operator, от которого наследуются уже конкретные операторы, причём сразу в описании можно задавать аргументы для конструктора. Кстати, члены по умолчанию immutable, что позволяет при работе с классами не нарушать функциональный стиль.
Для вызова метода класса вместо точки как в C++ используется решётка #. Например для печати оператора op нужно вызвать конструкцию print_string op#to_string;.
Если внимательно посмотреть на объявление класса, можно заметить, что типы переменных в конструкторе заданы напрямую: class operator (assoc:association) (priority:int) (str:string). Это связано с тем, что по имеющейся информации о классе, а это только getter нельзя вывести тип члена. Хотя, казалось бы, зачем он здесь нужен?
Неприятным сюрпризом оказалось то, что названия функций и классов _обязаны_ начинаться с маленькой буквы. Не очень хорошее ограничение для плюсистов, часто пишущих имена классов с заглавной.
Сам алгоритм вызывается строчкой (transformTokens tokenList). Занимает он ~45 строк против ~130 плюсового примера википедии. Вместо обхода списка циклом использована характерная для ФП хвостовая рекурсия, что пожалуй позволяет несколько лучше понимать каждый следующий шаг алгоритма.
В общем пока что мне OCaml больше нравится чем нет.
P.S. На самом деле пример из википедии не показатель, ибо написан он совершенно убого и даже не на C++.
Итак, для начала сама прога лежит здесь в папке ocaml_my_example. Писалась она специально в функциональном стиле, поэтому стек здесь реализован через список, а не взят из стандартной библиотеки. Для проверки результата использовалась программа на C++, лежащая на википедии.
Исполнение программы начинается со строк
let line = read_line()
in let stringList = Str.split (Str.regexp "[ \t]+") line
in let tokenList = List.map string_of_token stringList
in List.map print_token (transformTokens tokenList);;
print_newline ();;
которые считывают строку, преобразуют её в список токенов и выводят результат работы алгоритма. Слово in позволяет ограничивать область видимости имени только для следующих объявлений, что весьма удобно.
Перед запуском алгоритма необходимо преобразовать строку в токены. Я сделал довольно примитивную функцию (делать сложную смысла нет), которая выглядит примерно так:
(*translating string into token*)
let string_of_token = fun str ->
match str with
"+" -> Operator (new plus);
| "-" -> Operator (new minus);
| "*" -> Operator (new mult);
| "/" -> Operator (new div);
| "%" -> Operator (new mod_op);
| "!" -> Operator (new not);
| "=" -> Operator (new eq);
| "," -> Comma;
| "(" -> LBracket;
| ")" -> RBracket;
| _ as name when ((String.get name 0) >= 'A') && ((String.get name 0) <= 'Z') -> Operator (new func name);
| _ -> Digit (int_of_string str);
;;
В ocaml часто вместо стандартной условной конструкции удобно использовать pattern matching, что видно из примера. В нём строка str проверяется на соответствии одному из шаблонов и в зависимости от этого создаётся новый объект. Шаблон _ означает любое значение (привет, prolog).
А теперь на радость плюсистам: объекты! Да, они здесь есть, есть член-функции и есть наследование. В общем сколько-нибудь инкапсуляцию и полиморфизм обеспечить возможно. Собсна, вот как это выглядит:
(*base class for all operators*)
class operator (assoc:association) (priority:int) (str:string) =
object
val assoc_ = assoc
val priority_ = priority
val string_ = str
method assoc = assoc_
method priority = priority_
method to_string = string_
end;;
(*operators itself*)
class eq = object inherit operator Right 1 "=" end;;
class plus = object inherit operator Left 2 "+" end;;
class minus = object inherit operator Left 2 "-" end;;
class mult = object inherit operator Left 3 "*" end;;
class div = object inherit operator Left 3 "/" end;;
class mod_op = object inherit operator Left 3 "%" end;; (*because 'mod' is keyword*)
class not = object inherit operator Right 4 "!" end;;
class func name = object inherit operator Right 5 name end;; (*when using a function you should give it name*)
Базовый класс operator, от которого наследуются уже конкретные операторы, причём сразу в описании можно задавать аргументы для конструктора. Кстати, члены по умолчанию immutable, что позволяет при работе с классами не нарушать функциональный стиль.
Для вызова метода класса вместо точки как в C++ используется решётка #. Например для печати оператора op нужно вызвать конструкцию print_string op#to_string;.
Если внимательно посмотреть на объявление класса, можно заметить, что типы переменных в конструкторе заданы напрямую: class operator (assoc:association) (priority:int) (str:string). Это связано с тем, что по имеющейся информации о классе, а это только getter нельзя вывести тип члена. Хотя, казалось бы, зачем он здесь нужен?
Неприятным сюрпризом оказалось то, что названия функций и классов _обязаны_ начинаться с маленькой буквы. Не очень хорошее ограничение для плюсистов, часто пишущих имена классов с заглавной.
Сам алгоритм вызывается строчкой (transformTokens tokenList). Занимает он ~45 строк против ~130 плюсового примера википедии. Вместо обхода списка циклом использована характерная для ФП хвостовая рекурсия, что пожалуй позволяет несколько лучше понимать каждый следующий шаг алгоритма.
В общем пока что мне OCaml больше нравится чем нет.
P.S. На самом деле пример из википедии не показатель, ибо написан он совершенно убого и даже не на C++.
Комментариев нет:
Отправить комментарий
Примечание. Отправлять комментарии могут только участники этого блога.