tag:blogger.com,1999:blog-72808097862420579102024-03-13T05:39:44.946+03:00Mind -> [Thought] -> IO Blog -- Here I am(setf *my-blog* (make-instance 'blog)) ;Also available in Common LISPCrazy_Owlhttp://www.blogger.com/profile/01391696075679047126noreply@blogger.comBlogger8125tag:blogger.com,1999:blog-7280809786242057910.post-85709788772802868972010-12-09T19:46:00.004+03:002010-12-10T19:44:18.721+03:00Clojure - пишем программу с базовым интерфейсом и собираем ее в .jar<div>Переписал пример из "Core Java. Fundamentals" на clojure, чтобы разобраться с тем, как на ней пишется GUI.</div><div>Код и комментарии - под катом.</div><div><a name='more'></a>Вот код:</div><div style="background-color: darkslategrey; color: wheat;"><style type="text/css">
<!--
.builtin {
/* font-lock-builtin-face */
color: #98fb98;
font-weight: bold;
}
.function-name {
/* font-lock-function-name-face */
color: #7b68ee;
font-weight: bold;
}
.keyword {
/* font-lock-keyword-face */
color: #fa8072;
}
.string {
/* font-lock-string-face */
color: #ffa07a;
font-style: italic;
}
.type {
/* font-lock-type-face */
color: #9acd32;
font-weight: bold;
}
a {
color: inherit;
background-color: inherit;
font: inherit;
text-decoration: inherit;
}
a:hover {
text-decoration: underline;
}
-->
</style><br />
<pre>(<span class="keyword">ns</span> interfacetest.core
(<span class="builtin">:import</span> [javax.swing
JFrame
JLabel
JFileChooser
JMenuBar
JMenuItem
JMenu
ImageIcon]
[java.io
File]
[java.awt.event
ActionListener])
(<span class="builtin">:gen-class</span>))
(<span class="keyword">defn</span> <span class="function-name">create-listener</span> [lmbd]
(<span class="builtin">proxy</span> [ActionListener]
[]
(actionPerformed [e] (lmbd e))))
(<span class="keyword">defn</span> <span class="function-name">initframe</span> [<span class="type">#^JFrame</span> frame]
(<span class="keyword">let</span> [lbl (JLabel.)
chooser (JFileChooser.)
menubar (JMenuBar.)
menu (JMenu. <span class="string">"File"</span>)
openItem (JMenuItem. <span class="string">"Open"</span>)
exitItem (JMenuItem. <span class="string">"Exit"</span>)]
(<span class="keyword">doto</span> openItem
(.addActionListener
(create-listener
(<span class="builtin">fn</span> [e] (<span class="keyword">let</span> [result (.showOpenDialog chooser nil)
getname (<span class="builtin">fn</span> [] (<span class="keyword">..</span> chooser getSelectedFile getPath))]
(<span class="keyword">if</span> (<span class="builtin">=</span> result JFileChooser/APPROVE_OPTION)
(.setIcon lbl (ImageIcon. (getname)))))))))
(<span class="keyword">doto</span> exitItem
(.addActionListener
(create-listener
(<span class="builtin">fn</span> [e] (System/exit 0)))))
(.setCurrentDirectory chooser (File. <span class="string">"."</span>))
(<span class="keyword">doto</span> menu
(.add openItem)
(.add exitItem))
(.add menubar menu)
(<span class="keyword">doto</span> frame
(.setDefaultCloseOperation JFrame/EXIT_ON_CLOSE)
(.setSize 300 300)
(.setVisible true)
(.add lbl)
(.setJMenuBar menubar))))
(<span class="keyword">defn</span> <span class="function-name">-main</span> []
(<span class="keyword">let</span> [fr (JFrame. <span class="string">"Hello world"</span>)]
(initframe fr)))
</pre></div><div>Результатом работы программы будет небольшая формочка с двумя пунктами меню - "Open" и "Exit". Open предложит выбрать изображение, которое и будет помещено на формочку (изображение не ресайзится, от очень большой картинки будет показана только часть.<br />
</div><div>Разберем код по порядку.<br />
</div><div>Инструкция (ns) задает пространство имен под названием interfacetest.core. В ней же указано, что необходимо сгенерировать Java-класс для этого пространства имен (что и будет сделано при компиляции) - :gen-class. В директиве :import подключаются классы из стандартной библиотеки Java (написанные не на Clojure).<br />
</div><div>Функция create-listener превращает функцию clojure в объект, реализующий интерфейс ActionListener. Эти объекты понадобятся нам позже, для меню.<br />
</div><div>Далее следует функция initframe, принимающая объект класса JFrame (инструкция #^JFrame заставляет делать проверку типов при компиляции, если я ничего не путаю). В let-биндинге создаются объекты JMenuBar, JFileChooser, JMenuItem и так далее, а затем, при помощи инструкций doto производятся действия над этими объектами и собирается интерфейс. <pre>(doto object ...)</pre>позволяет производить несколько действий подряд над одним и тем же объектом. Обычно вызов методов объектов производится так: <pre>(.method object args)</pre>, в случае с doto можно делать так: <pre>(doto object (.method args) ... )</pre>, чем мы и пользуемся.<br />
</div><div>Затем определяется метод -main, который будет вызываться java'ой. В нем мы создаем фрейм и вызываем для него initframe.<br />
</div><div>Да, еще момент - я использовал в let-биндинге лямбда-функцию, чтобы отложить получение имени выбранного файла, потому что не был уверен, что на момент биндинга оно будет существовать. Такая вот ленивость.<br />
</div><div>Вроде все. Теперь про компиляцию.<br />
</div><div>Пространство имен называется interfacetest.core, значит, создаем на диске директорию interfacetest и кладем туда файл core.clj с указанным выше содержимым. Затем переходим в командной строке в директорию уровнем выше interfacetest, и запускаем REPL Clojure. В интерпретаторе пишем:<br />
</div><div><pre>(compile 'interfacetest.core)</pre></div><div>Файл откомпилирован, и на диске создана директория classes, в которой лежат interfacetest/*.class. Проверить, что все получилось, можно следующим образом - из командной строки переходим в директорию classes (желательно положить туда же clojure.jar) и пишем:<br />
</div><div><pre>java -cp .;clojure.jar interfacetest.core</pre></div><div>Обратите внимание - здесь указана версия для Windows - разделитель ";" в classpath. Для линукса нужно будет заменить ";" на ":".<br />
</div><div>Осталось только собрать наш jar. Распаковываем clojure.jar как обычный .zip-архив, копируем директорию clojure (ту, которая лежит в директории распаковки), кладем ее рядом с interfacetest и пишем:<br />
</div><div><pre>jar cvfe jarfilename.jar interfacetest.core clojure\* interfacetest\*</pre></div><div>Опять же, это версия для Windows - для линукса заменяем обратные слеши на прямые. Получаем исполняемый файл, который запускается без лишних библиотек.<br />
</div><div>Вроде все. За кадром остался вопрос, как определить только используемые классы из clojure.jar и не включать из в итоговый jar, тем самым уменьшая его размер (думаю, это можно сделать).<br />
</div>Crazy_Owlhttp://www.blogger.com/profile/01391696075679047126noreply@blogger.com5tag:blogger.com,1999:blog-7280809786242057910.post-68794905171585928802010-09-27T19:03:00.001+04:002010-12-10T19:36:02.344+03:00Я вернулся.Не знаю, надолго ли, но вернулся, гыгы.<br />
<br />
Написал на хаскеле интерпретатор конечных автоматов Мили, аналогичный используемому в институте "МИЭМ-89" (написанному на паскале). На все про все ушло около двух вечеров: один разбирался с парсерами и их комбинаторами и писал свой велосипед (хотя, наверное, стоило бы повтыкать в Parsec), а второй - писал собственно интерпретатор.Crazy_Owlhttp://www.blogger.com/profile/01391696075679047126noreply@blogger.com1tag:blogger.com,1999:blog-7280809786242057910.post-47715202881069483082010-09-27T19:01:00.001+04:002010-12-10T19:36:09.316+03:00Устанавливаем SDL-ные биндинги поверх Haskell Platform for WindowsНаткнулся на такую ссылку: <a href="http://web.animal-machine.com/blog/2010/04/a-haskell-adventure-in-windows/">http://web.animal-machine.com/blog/2010/04/a-haskell-adventure-in-windows/</a>Crazy_Owlhttp://www.blogger.com/profile/01391696075679047126noreply@blogger.com0tag:blogger.com,1999:blog-7280809786242057910.post-56564171490908265802010-06-25T13:46:00.002+04:002010-12-10T19:36:17.541+03:00Няшный Haskell.На днях нарыл книжку "Programming in Haskell", начал потихоньку разбираться. Пока что понимаю не так уж много, но работаю над собой. Налицо, кстати, явное сходство F# с сабжем - afaik, одни и те же люди приложили руку к созданию как того, так и другого.<br />
<br />
А сейчас - про общую, хех, "няшность" брутального Хаскеля. <a href="http://raincat.bysusanlin.com/">Игру про няшного котика, написанную на Haskell</a>, все видели? Ну а теперь вот второе наблюдение - интерпретатор Хаскеля называется "hugs". Вот сидит такой одинокий программист, захочется ему обнять кого-нибудь, открывает он консоль и пишет там "обнимашки" (ну, hugs пишет, имею ввиду). И запускается ему интерпретатор, и теплеет у него на душе, и с блаженной улыбкой он начинает работать.Crazy_Owlhttp://www.blogger.com/profile/01391696075679047126noreply@blogger.com2tag:blogger.com,1999:blog-7280809786242057910.post-79403118472503562472010-04-19T18:16:00.005+04:002010-12-10T19:36:52.613+03:00F# - красивая конструкцияСижу, пишу сервер. Есть у меня две функции, которые принимают данные разных типов (String и byte array) и отправляют их в поток - первая, соответственно, для отправки клиенту текста (заголовки всякие, прочая белиберда), вторая - для отправки изображений и прочей бинарщины.<br />
<br />
И вот, благодаря паттерн матчингу, их можно объединить:<br />
<br />
<pre>let sendToStream (str: NetworkStream) (bin: Object) =
match bin with
| :? string as strn ->
str.Write(System.Text.Encoding.UTF8.GetBytes(strn), 0, strn.Length)
| :? (byte[]) as bytarr ->
str.Write(bytarr, 0, bytarr.Length)
</pre><br />
Вот такая вот функция. Что радует - увеличившийся уровень абстракции - стоит появиться какому-нибудь новому типу, для которого нужен свой способ отправки - все, что нужно изменить - паттерн. Благодаря наличию Object на верхнем уровне мы можем совать какие угодно данные в функцию. Правда, тут стоит следить за наследованием - ведь проверка типов может не дойти до нашего "нового" типа. Поэтому наиболее частные типы следует размещать как можно раньше.<br />
<br />
P.S. Кстати, в приведенном примере компилятор найдет изъян - не покрыты все паттерны. Дело лечится добавлением граничного случая <code>| _ -> failwith "Invalid input"</code> например.Crazy_Owlhttp://www.blogger.com/profile/01391696075679047126noreply@blogger.com2tag:blogger.com,1999:blog-7280809786242057910.post-78967752131751255272010-04-16T22:51:00.007+04:002010-12-10T19:37:02.715+03:00Erlang - шифр цезаря.Рад вам представить результат двух вечеров мучений - *барабанная дробь* - шифр Цезаря (а точнее, аффинные преобразования его же имени) на эрланге.<br />
<br />
О том, что из этого получилось - под катом.<br />
<br />
<br />
<a name='more'></a><br />
<br />
Итак, изначально идея проста - есть функция, которая кодирует некий символ из алфавита путем арифметических действий (умножение и сложение) над его порядковым номером в этом алфавите. Получившееся число делится по модулю на длину алфавита - и получаем порядковый номер нового символа.<br />
При реализации упор я сделал на то, что каждый символ кодируется _одним_ целым числом. В процессе кодирования выяснилось, что одно число - это unicode codepoint, а на самом деле символ кириллицы представляется в виде двух двоичных чисел (латиница и цифры - по-прежнему одним).<br />
<br />
Итак, реализация и элегантные костылики:<br />
<br />
<br />
Модуль unic - переводит список вида [208,179....] (именно список, а не бинарную последовательность) в список codepoint'ов:<br />
<br />
UPD: хтмл-парсер съел немного, <a href="http://pastebin.com/ZBjbBCeF">выкладываю с пастебина</a><br />
<br />
<a href="http://pastebin.com/2ZErLSze">Кодирование</a><br />
<br />
В консоли стартуем сервер: caesar:init(), после чего кодируем сообщения: caesar:enc("Сообщение").<br />
<br />
Вот такие велосипеды.Crazy_Owlhttp://www.blogger.com/profile/01391696075679047126noreply@blogger.com3tag:blogger.com,1999:blog-7280809786242057910.post-26157039605027651562010-03-27T23:51:00.006+03:002010-12-10T19:38:24.609+03:00F# Mono Bug, или Микрософт как всегда.Наткнулся сегодня на интересный баг в F#.<br />
До конца локализовать пока не удалось, но, кажется, что паттерн матчинг с 'a option неправильно работает.<br />
Итак. Имеем код (да, это тот самый шифр цезаря :] ):<br />
<br />
<code><br />
module Caesar<br />
<br />
let alphabet = Array.ofSeq "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890.,?!+=/"<br />
<br />
let getIndex (arr: 'a[]) (elt: 'a) =<br />
let optIndex = arr |> Array.tryFindIndex( (=) elt )<br />
match optIndex with<br />
| Some(x) -><br />
printfn "found %A" x<br />
x<br />
| None -><br />
printfn "None"<br />
-1<br />
<br />
let encodeLetter (shift: int) (letter: char) =<br />
let idx =<br />
letter<br />
|> getIndex alphabet<br />
printfn "%c - %d" letter idx<br />
alphabet.[<br />
( idx + shift ) % (Array.length alphabet)<br />
]<br />
</code><br />
<br />
В винде все работает, как и предполагалось - написав в fsi, к примеру<br />
<br />
#load "caesar.fs"<br />
Caesar.encodeLetter 5 'a';;<br />
<br />
Получим на экране<br />
<br />
found 0<br />
a - 0<br />
val it : char = 'f'<br />
<br />
То есть - getIndex отработал как надо, достал нам индекс, что, в свою очередь означает, что паттерн матчинг Some(x) сработал. Под моно же имеем следующую картину:<br />
<br />
None<br />
a - -1<br />
val it : char = 'e'<br />
<br />
То есть явно видно, что в том же самом коде тот же самый паттерн не сработал.<br />
<br />
<del>Бьюсь в истерике</del> Очень интересный баг.<br />
<br />
<i><br />
UPD: Однако, если вызывать getIndex напрямую - выводит все как полагается. Значит, беспорядки начинаются уже в encodeLetter - почему-то "теряется"... пока не пойму что.<br />
<br />
UPD2: Причина, оказывается, крылась в генерализации функции (я явно указал 'a в качестве типа параметра). Как только поменял типы параметров в getIndex на char - заработало и под Mono. Подозреваю, тут что-то связано с выводом типов. Буду ковырять дальше.<br />
<br />
UPD3: У Mono с выводом типов вообще как-то не очень ладится. Не могли сделать все как в дотнет.<br />
</i>Crazy_Owlhttp://www.blogger.com/profile/01391696075679047126noreply@blogger.com2tag:blogger.com,1999:blog-7280809786242057910.post-10197349040637842312010-03-09T11:05:00.002+03:002010-12-10T19:38:59.497+03:00Компилируем бинарники из лиспа - SBCL и clispДавно не писал, потому что пока что почти все вечера у меня заняты погружением в Коммон Лисп.<br />
<br />
Пощупал Clojure, и она мне, в целом, понравилась, если бы не одно "но" - совершенно неочевидные внутренние механизмы джавы, благодаря которым я вообще не понимаю, что к чему, что это за classpath, зачем он нужен, почему его надо явно указывать и с какой стати мой простейший скрипт на две строчки тормозит полторы секунды перед выполнением. Конечно же, если вникнуть во внутреннее устройство джавки и научиться компилировать мои clojure-произведения в *.jar, все будет работать быстрее. Но это впереди, а пока что я решил закончить-таки Practical Common LISP.<br />
<br />
И сегодня я расскажу, как компилировать бинарник в SBCL и clisp.<br />
<br />
Итак, начнем с SBCL. Подготовив наш файл к запуску, а именно - написав функцию, которая будет запускаться первой (этакий int main() :] ), мы грузим sbcl-овский REPL и пишем там:<br />
<br />
<code>(load "our-file-name")</code><br />
<br />
Получив в ответ "Т", мы, в принципе, готовы приступать к компиляции. Вводим в REPL:<br />
<br />
<code>(sb-ext:save-lisp-and-die "binary-file-name" :executable t :toplevel 'function-name)</code><br />
<br />
По идее, функция #'save-lisp-and-die просто сохраняет состояние интерпретатора лиспа, если бы не... параметр :executable вместе с :toplevel. Они содержат явное указание - скомпилировать бинарник, при запуске которого выполнится функция function-name. В качестве дополнительного требования к function-name sbcl просит явно вызывать функцию (quit).<br />
<br />
Итак, когда я попробовал указанные выше манипуляции впервые и получил результат, радости моей не было предела. До тех пор, пока я не взглянул на размер бинарника - а он оказался, ни много ни мало, 28 мегабайт. Ага, для простенького скрипта. Ну, это остается на совести создателей SBCL, тем более, думаю, вряд ли сейчас для кого-то проблема бинарники в 20+ мег. Впрочем, глубоко этот вопрос я не ковырял, скорее всего, можно и сократить объем файла.<br />
<br />
Теперь перейдем к clisp:<br />
<br />
<code>(load "our-file-name")<br />
(ext:saveinitmem "binary-file-name" :executable t :init-function 'function-name :quiet t)</code><br />
<br />
Здесь видим, что :toplevel меняется на :init-function и, кроме того, добавляется параметр :quiet, который препятствует показу баннера clisp перед запуском вашей программы. Меня такие вещи бесят. Кому нравится - не указывайте :quiet вообще.<br />
<br />
Добавлю, что ваша программа должна содержать вызов функции #'ext:exit, чтобы она нормально завершила работу (иначе после того, как ваша программа завершится, пользователю покажут clisp-овский REPL).<br />
<br />
Бинарник таким образом получается всего 5,5 мег.<br />
<hints id="hah_hints"></hints>Crazy_Owlhttp://www.blogger.com/profile/01391696075679047126noreply@blogger.com4