воскресенье, 7 ноября 2010 г.

Хочу стать бездушной тварью, кто-нибудь знает способ?
Всем похер.

Делай что должен - Улыбнись и иди дальше.

среда, 27 октября 2010 г.

Upd. Паттерн матчинг

В догонку к предыдущему сообщению.


let find = function
    | "by" :: "name" :: _ :: []  -> 3
    | "by" :: "autor" :: _ :: [] -> 4
    | "by" :: "autors" :: _ :: [] -> 5
    | _ -> 0
и 
let find = function
    | "by" :: "name" :: t when t <> [] -> 3
    | "by" :: "autor" :: t when t <> [] -> 4
    | "by" :: "autors" :: t when t <> [] -> 5
    | _ -> 0
Так вот, 2й вариант значительно хуже! Он развернётся аж в две функции, каждая из которых будет сложнее того, во что развернётся 1й варинт(листинги приводить не буду - всё это можно увидеть в рефлекторе).

Паттерн матчинг и выделение памяти

Есть небольшой и довольно простой код:


let replace = function
    | "by" :: "id" :: t :: [] -> 8
    | _ -> 0
t :: [] - используется для того, чтобы гарантировать наличие одного свободного эл-та в списке.

И вроде бы всё хорошо, но если посмотреть в рефлекторе, то можно увидеть такую картину:

public static int replace(FSharpList<string> _arg1)
{
    if (_arg1.get_TailOrNull() != null)
    {
        FSharpList<string> list = _arg1;
        if (string.Equals(list.get_HeadOrDefault(), "by") 
&& (list.get_TailOrNull().get_TailOrNull() != null))
        {
            FSharpList<string> list2 = list.get_TailOrNull();
            if (string.Equals(list2.get_HeadOrDefault(), "id") 
&& (list2.get_TailOrNull().get_TailOrNull() != null))
            {
                FSharpList<string> list3 = list2.get_TailOrNull();
                if (list3.get_TailOrNull().get_TailOrNull() == null)
                {
                    string t = list3.get_HeadOrDefault();
                    return 8;
                }
            }
        }
    }
    return 0;
}
Выделенная строка намекает на то, что выделяется память под ту же ссылку(а это лишняя работа).

Но, если использовать _ вместо t, то всё становится несколько радужней:

public static int replace(FSharpList<string> _arg1)
{
    if (_arg1.get_TailOrNull() != null)
    {
        FSharpList<string> list = _arg1;
        if (string.Equals(list.get_HeadOrDefault(), "by") 
&& (list.get_TailOrNull().get_TailOrNull() != null))
        {
            FSharpList<string> list2 = list.get_TailOrNull();
            if ((string.Equals(list2.get_HeadOrDefault(), "id") 
&& (list2.get_TailOrNull().get_TailOrNull() != null)) 
&& (list2.get_TailOrNull().get_TailOrNull().get_TailOrNull() == null))
            {
                return 8;
            }
        }
    }
    return 0;
}

Как можно заметить код становится значительно проще(пропадает один вложенный иф + не выделяется память под t + не вычисляется list3).

Отсюда нехитрый вывод - следует, по возможности, использовать запись вида h :: _ :: t вместо h :: elem :: t.

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

mutable привязки, интерпретатор и массивы

Есть такой код:

let mutable x =
    [|
     1; 2; 3; 4; 5;
     2; 3; 5; 6; 3;
     |]
 
let to_null =
    for i in 0..9 do
        x.[i] <- 0
загоняем его в интерпретатор и получаем соотв.:

val mutable x : int [] = [|0; 0; 0; 0; 0; 0; 0; 0; 0; 0|]
val to_null : unit = ()


Всё правильно, всё логично.

А теперь снова засунем в интерпретатор x:
val mutable x : int [] = [|1; 2; 3; 4; 5; 2; 3; 5; 6; 3|]

и вызовет to_null:

> to_null;;
val it : unit = ()
> x;;
val it : int [] = [|1; 2; 3; 4; 5; 2; 3; 5; 6; 3|]


Поэтому следует быть осторожным при работе с mutable привязками в интерпретаторе и, по крайней мере, перезагружать их при каждой загрузке кода в fsi.

пятница, 15 октября 2010 г.

Динамические системы в F# - задание, динамика

Решил начинать потихоньку писать код по ДС на F#. Оказалось это очень удобно и невообразимо просто, если знать что такое карринг;)

Итак, ДС состоит из двух элементов:

  1. Множество состояний S;
  2. Функция переходов(эволюции) f : S -> S.
очевидным образом можно примерно получить описание такого объекта на F#:
Set<'a> * ('a -> 'a),

т.е. это кортеж из множества и функции.

Ну с множеством всё понятно - это встроенный тип в F#, а вот с функцией не всё так гладко, т.к. не хочется каждый раз писать новую функцию для каждой ДС вручную.
Но, т.к. я рассматриваю КДС, выясняется, что любую функцию можно задать множеством пар вида (a,b), где a, b принадлежат S, а это можно представить как list<'a * 'a>.
Реализовал я это так:

let rec evo_function table a =
        match table with
            | h :: t ->
                if (fst h) = a then snd h else evo_function t a
            | _ -> failwith("dynamic error: invalid state")

Соответственно использоваться это будет следующим образом:

let ds1 = (Set[0;1], evo_function [(0,1);(1,0)])

т.е. создавая ДС я каррирую evo_function таблицей переходов функции и получаю нужную мне ДС.

Остаётся решить вопрос с созданием удобного способа динамики ДС. Но и эта проблема решается с помощью карринга легко и просто! Смотрите сами:

let evolution ds = Seq.unfold (fun state -> Some(state, state |> snd ds))

Элементарная функция создающая последовательность на основе псевдо-рекурсивных вызовов каррированной evo_function у ds. Плюс ко всему используя последовательность, как результат прогона динамики ДС я получаю выйгрыш в производительности за счёт того, что для последовательностей используются ленивые вычисления в F# :)

Вот полный пример инициализации ДС и получения для неё двух последовательных динамик:

> let ds1 = (Set[0;1], evo_function [(0,1);(1,0)]);;
val ds1 : Set * (int -> int) = (set [0; 1], )
> let evo_ds1 = evolution ds1;;
val evo_ds1 : (int -> seq)
> evo_ds1 1;;
val it : seq = seq [1; 0; 1; 0; ...]
> evo_ds1 0;;
val it : seq = seq [0; 1; 0; 1; ...]

Не правда ли элементарно? ;)

пятница, 8 октября 2010 г.

Удаление бесплодных и недостижимых символов из КС-грамматики


Не далее как сегодня утром я таки дошёл с флешкой и таки сдал эти задачки:)

Привожу тут код этих алгоритмов:

N, E - Set - мн-ва не\терминальных символов
P - Set> - мн-во правил грамматики


// функция построения Ni для DelSterileChar
    let rec del_char (N' : Set) : Set = 
        let Ni : Set = 
            Set.map (fun (a, b) -> 
                        if List.forall (fun e -> N'.Contains(e) || E.Contains(e)) b  
                            then a 
                            else "") P
            |> Set.filter (fun k -> k <> "")
        if Ni = N' then Ni
                   else del_char <| Set.union Ni N'
 
    // функци построения Ni для DelUnattChar
    let rec del_un_char (N' : Set) : Set =
        let Ni = Set.filter (fun (a, b) -> N'.Contains(a)) P
                 |> Set.fold (fun acc (a, b) -> b @ acc) []
                 |> Set.ofList
                 |> Set.union N'
        if Ni = N' then Ni
                   else del_un_char Ni
    
    // алгоритм удаления бесподных символов
    member x.DelSterileChar() : unit =
        N <- del_char <| Set []
        P <- Set.filter (fun (a, b) -> 
                            N.Contains(a) && 
                            List.forall (fun e -> N.Contains(e) || E.Contains(e)) b) P
                                
    // алгоритм удаление недостижимых символов
    member x.DelUnattChar() : unit =
        let Ni = del_un_char <| Set [S]
        N <- Set.intersect N Ni
        E <- Set.intersect E Ni
        P <- Set.filter (fun (a, b) -> Ni.Contains(a) &&
                                       List.forall (fun e -> Ni.Contains(e)) b) P