Как стать автором
Обновить

Интерпретатор для HQ9+ на F#

Время на прочтение 3 мин
Количество просмотров 1.1K
Пока полномасштабная статья про поддержку F# различных парадигм еще готовится к выпуску, постараюсь занять ваше внимание некоторыми приятными и не отягощающими мелочами.
Вот скажите, знаете ли вы, что есть на свете такой замечательный язык программирования, как HQ9+? А чем же он такой замечательный? Ну например, назовите еще хоть один язык, где легендарная программа «Hello, world!» состоит из одного символа. Заметьте, не строки, не оператора, а символа — «H». Более того, этот язык настолько хорош, что куайн для него записывается тоже одним символом -«Q».

Но и это еще не все. Третья уже почти классическая задача для начинающего (и не очень) программиста — вывод известного (и очень длинного) стихотворения про 99 бутылок… да-да, тоже осуществляется одним символом — «9». Ну и наконец, символ "+" увеличивает на единицу заданный в языке аккумулятор, который к сожалению, никак нельзя использовать — превратность судьбы.
Полное описание языка можно почитать на сайте его автора.
Таким образом, программа H+Q+QH выдаст вот такой результат:
Hello, world!
H+Q+QH
H+Q+QH
Hello, world!
А переменная будет равна 2.
Отличный язык, не правда ли? Но не волнуйтесь, я не буду учить вас на нем программировать. Вместо этого я всего лишь предложу свою версию интерпретатора этого волнующего эзотерического языка на F#. Алгоритм не блещет особой теоретической новизной, но позволяет еще разок оценить силу pattern matching'а, который здесь используется чуть менее, чем повсеместно. Смотрите сами:
#light
open System
let input = Console.ReadLine()
let mutable inc = 0

let bottles k =
  match k with
  |0 -> "no more bottles"
  |1 -> "1 bottle"
  |_ -> k.ToString() + " bottles"

let rec poem k =
  match k with
  |0 -> printfn "No more bottles of beer on the wall, no more bottles of beer.\r\nGo to the store and buy some more, 99 bottles of beer on the wall."
  |_ -> printfn "%s of beer on the wall, %s of beer.\r\nTake one down and pass it around, %s of beer on the wall." (bottles k) (bottles k) (bottles (k-1))
     poem (k-1)

let parse ch =
  match ch with
  |'H'|'h' -> printfn "Hello, world!"
  |'Q'|'q' -> printfn "%s" input
  |'+' -> inc <- inc+1
  |'9' -> poem 99
  |_ -> ()
  
input |> Seq.iter parse
printfn "Debug information: Accumulator = %d" inc 
let wait = Console.ReadKey()




Программу на HQ9+ мы будем читать из консоли, для чего и подключаем библиотеку System (вместо сишаропвоского using здесь используется open).
Первые две функции служат для самой на поверку сложной операции — вывода стихотворения про бутылки. Первая определяет правильную форму окончаний в зависимости от числа оставшихся бутылок, вторая выдает полное правильное двустишие и как уже мы привыкли, отправляется в рекурсию.
Третья функция основная и производит некоторые действия над выданным ей символом. Как мы помним, сама строка является последовательностью символов, так что для того чтобы парсить отдельные символы, попросту применяем к ней метод Seq.iter с определенной функцией в качестве параметра.
К слову, думаю кое у кого, не сталкивавшегося ранее с функциональным программированием может возникнуть резонный вопрос, чем отличается Seq.iter от Seq.map (а также List.iter и Arr.iter от соответствующих map). На первый взгляд, обе они применяют некоторую функцию ко всем элементам последовательности. Но отличия есть, причем весьма критические. Для того, чтобы это понять, достаточно посмотреть на сигнатуру:

val iter: ('a -> unit) -> seq<'a> -> unit
val map: ('a -> 'b) -> seq<'a> -> seq<'b>



Тип unit в F# означает то же, что и void в C#. Таким образом, Map переводит одну последовательность в другую, в соответствии с некоторой функцией преобразования, Iter же применяет к элементам последовательности некоторую функцию, смысл которой в сайд-эффекте, чем конечно нарушает догматы ФП, но позволяет просто производить некоторые рутинные операции, например вывод.
Еще одно нарушение ФП, о котором более полно, как уже и обещалось, в следующий раз — это ключевое слово mutable при определении функции-«переменной» inc. Теперь мы можем переопределять ее во время выполнения программы с помощью символа <-.
Вот такой простой язык программирования HQ9+, и не менее простой к нему интерпретатор.
Ну и раз уж речь зашла о куайнах, то надо что ли привести куайн на самом F#. Он к сожалению, не так короток, как на HQ9+.
#light
open System
let s = "#light
open System
let s = {0}{1}{0}
Console.Write(s,{0},s)"

Console.Write(s,'"',s)



Думаю, даже на глазок видно, что программа действительно выдает свой код.
Пожалуй, не очень приятно в этом куайне то, что он использует фреймворковские Console.Write вместо родных printf, но в данном случае они действительно удобнее, а ведь должно же наличие .Net давать нам преимущества над тем же OCaml. Вот мы ими и воспользуемся.
Вот вроде бы и все на сегодня.
Теги:
Хабы:
+6
Комментарии 2
Комментарии Комментарии 2

Публикации

Истории

Ближайшие события

Московский туристический хакатон
Дата 23 марта – 7 апреля
Место
Москва Онлайн
Геймтон «DatsEdenSpace» от DatsTeam
Дата 5 – 6 апреля
Время 17:00 – 20:00
Место
Онлайн