пятница, 31 августа 2012 г.

Система модулей в Julia

Т.к. в основной документации Julia нету описания модулей, то сделаю маленький обзор исходя из этого топика рассылки.

Для создания модуля используется следующая конструкция:

module ModuleName
    #Some code
end

Просто и очевидно.

Теперь рассмотрим такой код:

module HELLO
    print("hello")  # Ошибка!
end

Должно работать? А вот и нет. Теперь стандартные функции перенесены в модуль Base, который не импортируется по умолчанию, поэтому Julia скажет: "print not defined".

Правильно будет:

module HELLO
    import Base.*
    print("hello")
end

Что значит .* в данной записи? А значит оно вот что: "Из модуля Base экспортируются все символы, которые помечены как export". Как использовать export в своих модулях? Элементарно, пример:


module HELLO
    import Base.*
    export hello

    function hello()
        print("hello")
    end
end

Стоит заметить, что export может находится в любом месте модуля, а не только в начале.

Экспортировать можно и отдельные символы с помощью конструкций вида import  Foo.foo.

Так же стоит заметить, что в одном файле исходного кода может быть несколько модулей (в отличие от python).

Следует указать ещё одну интересную возможность модулей - ключевое слово include.

module TEST
    import Base.*


    include("foo.jl")
    include("baz.jl")
end


В модуле TEST будут присутствовать все символы, определяемые в foo.jl и baz.jl. Причём никто не запрещает делать include("some_file.jl") сразу в нескольких модулях.

И ещё один пример:

module T
    import Base.*
    export t1, t2

    function t1()
        print("t1")
    end

    function t2()
        print("t2")
    end
end


module T2
    import T.*

    export t2
    t1()
end


module Z
    import T2.*

    t2()
end


А вот Z.t1() будет ошибочно. Почему? Потому что всегда требуется явный экспорт символов.

суббота, 3 марта 2012 г.

Вернулся из спячки

Что-то я давно ничего не писал. Буду исправляться.

За последнее время я слегка разочаровался в F# и находился в неком поиске себя и тех инструментов, которые мне было бы интересно изучить и использовать.

Наверно стоит сказать пару слов почему я отказался от F#.


  • Плохой компилятор. В общем то всё хорошо, но дьявол кроется в мелочах. И одна из этих мелочей -  rec. Нет, он честно раскрывается в цикл, когда может, вот только когда функция чуть побольше пары строк, то уследить за тем, чтобы все рекурсивные вызовы были правильно сделаны не всегда получается. Ну и следовательно приходится ловить StackOverflow exception в рантайме. Не весело в общем.
  • Синтаксис. Да он кажется красивым, но это ровно до того момента, пока ты не пишешь чего-либо больше сотни строк (и не вылизываешь их по пол дня). А чем больше кода, тем стремительнее он пытается превратиться в кашу. Из этого следуют две следующие проблемы.
  • Выразительность кода на F# иногда может сыграть с программистом в довольно плохую шутку. Выразительность усложняет код, т.е. вносить правку в F# код значительно сложнее, чем в C/C++/C#/python код. Не потому, что сложнее написать пару символов, а потому что приходится дольше вникать в кусок кода, чтобы понять, где нужно поправить.
  • Синтаксис F# сложен, при этом в компиляторе есть косяки и не все заявленные в том же MSDN фичи работают + проблемы с автовыводом типов (приходится иногда ручками прописывать, либо изменять код на аналогичный, но для которого типы выводятся). Это заставляется писать более "грязный" код.
В общем и целом я отношусь к F# положительно, но пока буду ждать, как будут развиваться компилятор и среда разработки под него. 

Ах да, на НГ игрался с Mailbox + ZMQ, получилось забавно, хотя пришлось дополнительно использовать стандартные Thread для запуска ZMQ сокетов, т.к. Mailbox довольно мутно рулит потоками и, по умолчанию, должен использовать однопоточную обработку сообщений.


В любом случае сейчас я с .Net и C#/F# не работаю. Пересел на Java + Clojure и раскуриваю Gephi и gephi-toolkit, хотя качество документации к ним не фонтан. Первые пробы Clojure оказались довольно неплохи. Важным оказалось то, что после F# я смог быстро адаптироваться к recur. Что же, попробую заставить себя перевести https://github.com/gephi/gephi-toolkit-demos на Clojure, главное чтобы запала хватило :)

четверг, 2 июня 2011 г.

Тестирование библиотек F# в Visual Studio

Когда создаётся новый проект F# Library, то в довесок в *.fs файлу идёт Srcipt.fsx. И в этот скрипт логично бы запилить функционал теста работы библиотеки, причём хорошо бы прогонять этот тест автоматически.

Оказывается это возможно и делается довольно легко и просто.

Итак, по по пунктам:

1. ПКМ на Script.fsx -> Properties -> Copy to Output Directory выставить в Copy Always. Это необходимо для того, чтобы исходный файл скрипта автоматически копировался при сборке в целевую директорию сборки.

2. Идёт в Project -> MyProjectName Properties -> Build Events. Нас интересует Post-build event command line. Прописываем там: fsi Script.fsx. Так же можно выставить по вкусу параметр Run the post-build events.

3. Добавляем в Script.fsx что-то вроде: printfn "Hello, World!", жмём F6 и переходим вкладку Output Visual Studio(View -> Output или Ctrl-W O) и видим примерно следующее:

Build started: Project: MyMathLib, Configuration: Debug Any CPU MyMathLib -> K:\common_projects\MyMathLib\MyMathLib\bin\Debug\MyMathLib.dll
fsi test.fsx
Hello, World!
Build: 1 succeeded or up-to-date, 0 failed, 0 skipped

Для того, чтобы прилинковать либу в скрипте используйте следующую инструкцию:
#r @"MyLibraryName.dll"

среда, 27 апреля 2011 г.

Генератор функций являющихся автоматами

Переоткрыл очередной велосипед.
Меня всегда раздражало то, что для задания автомата приходилось что-то примерно такого вида(mutable можно и на ref заменить, не суть важно):

type Aut(state: 'S) =
  let mutable st = state
  
  member this.Next(x: 'A): 'B =
    ...
    st <- ...
    result

т.е. делать отдельный класс под каждый автомат или класс обёртку. Мне хотелось чего-нибудь более элегантного и удобного, а именно - представления автомата функцией. Вот, сегодня накидал следующее:

let rec create (f: 'S -> 'I -> 'O) (g: 'S -> 'I -> 'S) (state: 'S) =
    let st = ref state
 
    fun (x: 'I) ->
      let r = f !st x
      st := g !st x
      r

т.е. передаём функции создателю 3 параметра: функцию выходов, функцию переходов и начальное состояние. Если передать 2 параметра, то, за счёт карирования получим не инициальный автомат, а если 3(или нач. стостояние в карированную ф-цию) - инициальный автомат.

Ну и пример работы:

let adder = 
  create 
    (fun s x -> 
      match s with
        | 0 -> x+1
        | 1 -> x
        | _ -> s+x
      ) 
    (fun s x -> (s+1) % 10) 0

printfn "%A" [
    for i in 0..20 ->
      adder 1
  ]

результат:
[2; 1; 3; 4; 5; 6; 7; 8; 9; 10; 2; 1; 3; 4; 5; 6; 7; 8; 9; 10; 2]

вторник, 28 декабря 2010 г.

Композиция функций и странные баги

Начал потихоньку вникать в теорию комбинаторов и решил попробовать их реализовать на F#.

Но наткнулся на один странный баг, который не смог воспроизвести дома. А именно, есть комбинатор функций a, b:

let (|>>) a b =
    fun x -> a x |> b

Так вот, используя интерпритатор F# из плагина к VS2008 я не мог построить комбинацию скажем таких функций:

let f1 x = x + 1
let f2 x = x.ToString()
 
Интерпритатор ругался и просил явно прописать типы.
хотя для
let f1 x = x + 1
let f2 x = x * x
 
или
 
let f2 x = (float) x + 1.0
 
комбинатор работал.
 
А вот дома, в VS2010 всё заработало в точности так, как и ожидалось.


Следующий код:

let (|>>) a b =
    fun x -> a x |> b
 
let f1 x = x + 1
let f2 x = (float) x + 1.0
let f3 x = [x]
 
let f' = f1 |>> f2 |>> f3
 
f' 10
 
выдал:
val ( |>> ) : ('a -> 'b) -> ('b -> 'c) -> 'a -> 'c
val f1 : int -> int
val f2 : int -> float
val f3 : 'a -> 'a list
val f' : (int -> float list)

> f' 10;;
val it : float list = [12.0] 
т.е. возможно есть какие-то проблемы с комбинаторами в старых версиях F#.
Но точно посмотреть версию F# на работе смогу только после НГ...
Всех с наступающим!)) 

четверг, 16 декабря 2010 г.

F# Snippets

Бродя по интернету в поисках инфы про quotations наткнулся у Томаса Петричека на следующую ссылку: http://fssnip.net/

более чем полезный ресурс, особенно если он будет и дальше развиваться.

вторник, 30 ноября 2010 г.

Скорость вычислений на F#

Недавно задумался над скоростью вычислений в F#, а вернее о том, стоит ли заморачиваться использую lazy() и seq. Поверхностные тесты seq показывают, что seq лучше для вычислений чем list:



> Seq.sum (seq{ for i in 1.0 .. 50.0 -> 1.0/(i*i*i) });;
Real: 00:00:00.001, CPU: 00:00:00.000, GC gen0: 0, gen1: 0, gen2: 0
val it : float = 1.201860863
> List.sum [ for i in 1.0 .. 50.0 -> 1.0/(i*i*i) ];;
Real: 00:00:00.002, CPU: 00:00:00.000, GC gen0: 0, gen1: 0, gen2: 0
val it : float = 1.201860863
> ;;
> ;;
> ;;
> Seq.sum (seq{ for i in 1.0 .. 50000.0 -> 1.0/(i*i*i) });;
Real: 00:00:00.008, CPU: 00:00:00.015, GC gen0: 0, gen1: 0, gen2: 0
val it : float = 1.202056903
> List.sum [ for i in 1.0 .. 50000.0 -> 1.0/(i*i*i) ];;
Real: 00:00:00.013, CPU: 00:00:00.015, GC gen0: 1, gen1: 0, gen2: 0
val it : float = 1.202056903
> List.sum [ for i in 1.0 .. 5000000.0 -> 1.0/(i*i*i) ];;
Real: 00:00:02.589, CPU: 00:00:02.870, GC gen0: 38, gen1: 26, gen2: 3
val it : float = 1.202056903
> Seq.sum (seq{ for i in 1.0 .. 5000000.0 -> 1.0/(i*i*i) });;
Real: 00:00:00.800, CPU: 00:00:00.795, GC gen0: 0, gen1: 0, gen2: 0
val it : float = 1.202056903

Причём чем больше эл-тов требуется вычислить, тем менее производительней становиться список, т.к. ему требуется подчищать за собой память оч. активно.


п.с. если использовать честный fold, то seq несколько замедляется, но всёравно в 2 раза быстрее списка:

> Seq.fold (fun acc e -> acc + e) 0.0 (seq{ for i in 1.0 .. 5000000.0 -> 1.0/(i*i*i) });;
Real: 00:00:01.125, CPU: 00:00:01.092, GC gen0: 0, gen1: 0, gen2: 0
val it : float = 1.202056903
> List.fold (fun acc e -> acc + e) 0.0 ([ for i in 1.0 .. 5000000.0 -> 1.0/(i*i*i) ]);;
Real: 00:00:02.716, CPU: 00:00:02.776, GC gen0: 35, gen1: 28, gen2: 1
val it : float = 1.202056903

правда стоит заметить, что для seq использование fold увеличило требуемое время на 0.3 сек, в то время когда для списка всего на 0.127 сек