среда, 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.