Соответствие схемы короткого замыкания F # - PullRequest
5 голосов
/ 05 августа 2011

Новый для F #.Следующий вопрос может не иметь никакого смысла вообще.

// an attempt at Huffman encoder 
let encodeValue x y = function ...

match ((encodeValue left value), (encodeValue right value)) with
    | ((true, encoded), (_, _)) -> (true, encoded + "1")
    | ((_, _), (true, encoded)) -> (true, encoded + "0")
    | ((false, _), (false, _)) -> (false, "")
    | _ -> failwith "Error"

В реальной среде encodeValue может быть довольно дорогим.Возможно ли (или разумно) попросить F # сначала оценить encodeValue left value, попробовать совпадения, а затем выполнить encodeValue right value при необходимости?

Ответы [ 4 ]

4 голосов
/ 05 августа 2011

Вы можете эмулировать короткое замыкание с ленивыми значениями и активными паттернами:

//not sure why function Lazy.force is recognized in VS but not in FSI
//need to use member Force()
let (|Force|) (l:Lazy<_>) =
    l.Force()

let encodeValue x y = function ...

match (encodeValue left value), lazy(encodeValue right value) with
    | (true, encoded), _                    -> (true, encoded + "1")
    | _              , Force(true, encoded) -> (true, encoded + "0")
    | (false, _)     , Force(false, _)      -> (false, "")
    | _                                     -> failwith "Error"

Lazy значения рассчитываются 0 или 1 раз: если вы никогда не наберете Force() для них, они никогда не будут рассчитаны. При первом вызове Force() они рассчитываются, а результат сохраняется при каждом следующем вызове Force().

(|Force|) вот полный активный шаблон, действительно полезная функция, которая позволяет вам реализовывать собственные структуры сопоставления шаблонов.

Обратите внимание, что @Brian указал, что вам нужно использовать _ в положении отложенного значения, где возможно короткое замыкание. Если (true, encoded) соответствует, тогда ленивый, дорогой расчет никогда не будет принудительным. Тогда для каждого другого случая при множественных совпадениях с использованием активного шаблона (|Force|) будет использоваться только результат первого инцидента.

Обновление

@ Дэниел указал, что F # уже имеет активный шаблон, который делает именно то, что (|Force|) делает: http://msdn.microsoft.com/en-us/library/ee340223.aspx

2 голосов
/ 05 августа 2011

Активное шаблонное решение намного круче, но я подумал, что, по крайней мере, стоит упомянуть, что простое вложенное совпадение также будет выглядеть вполне сносно:

  match encodeValue left value with
    | true, e -> true, e + "1"
    | false, _ -> 
      match encodeValue right value with
        | true, f -> true, f + "0"
        | _ -> false, ""
2 голосов
/ 05 августа 2011

Вот еще одно активное шаблонное решение. Передайте частично примененную функцию активному образцу:

let (|Encoded|_|) f x = 
  match f x with
  | true, encoded -> Some encoded
  | _ -> None

match value with
  | Encoded (encodeValue left) encoded -> (true, encoded + "1")
  | Encoded (encodeValue right) encoded -> (true, encoded + "0")
  | _ -> (false, "")

Я отбросил последний шаблон, потому что он никогда не будет совпадать.

1 голос
/ 05 августа 2011

Поскольку F # не ленивый функциональный язык (вы можете явно вычислить ленивые выражения). Что вы можете сделать, это сломать сопоставление с образцом, как показано ниже.

let a() = match encodeValue left value with
          | (true,encoded) -> (true,encoded + "1")
          | _ -> (false,"")
let b() = match encodeValue right value with
          | (true,encoded) -> (true,encoded + "0")
          | _ -> (false,"")
match a() with
| (false,_) -> b()
| r -> r
...