2014-02-20 4 views
1

Как можно выйти из раннего/разрыва/остановить создание массива в F # (в данном случае, Array2D.initBased)?Выйти/остановить Array2D.initBased early

Примечание:dic является Dictionary<,>(), значение которого является объектом, который имеет метод, названный someMethod, который принимает два int параметров.

let arr = Array2D.initBased 1 1 width height (fun x y -> 
    let distinctValues = dic |> Seq.map (fun (KeyValue(k,v)) -> v.someMethod x y) |> Set.ofSeq 
     match distinctValues.count with 
     | dic.Count -> 
      // do something 
      // exit array creation here, because I do not need arr any more if v.someMethod x y produced distinct values for each dic value 
     | _ -> 
      // do something else 
+0

Ваш выбор на самом деле не имеет для меня большого смысла. Вы берете 'dic', сортируете его, а затем создаете набор? Но set игнорирует порядок, поэтому значение 'distinctValues' будет одинаковым для каждого аргумента' x, y'. Это означает, что вы можете просто проверить свойство * перед *, вы начинаете строить массив. Итак, я думаю, что что-то не так ... –

+0

'someMethod' использует свойство объекта' v' кроме x и y, которое отличается для каждого значения значения 'dic', то есть' v', что приводит к возможно, разные 'distinctValues'. Таким образом, преобразование словаря <,>() '' dic' в 'Set'' distinctValues' имеет смысл. – prokilomer

+0

Я бы это понял, если он использовал 'Seq.map', но не с' Seq.sortBy' - даже если результат 'someMethod' отличается для каждой итерации (что он делает), вы используете это только для повторного заказа элементы - но упорядочение не имеет значения, если вы превратите это в набор в конце. –

ответ

2

Это сложный вопрос - я не думаю, что есть какая-либо функция, которая позволяет вам делать это легко. Я думаю, что лучший вариант - это, вероятно, определить вашу собственную функцию более высокого порядка (реализованную с использованием не очень элегантной рекурсии), которая скрывает поведение.

Идея заключается в том, чтобы определить tryInitBased, что ведет себя как initBased но функция пользователя, может вернуться вариант (чтобы указать отказ) и функция возвращает параметр (либо успешно создан массив или None):

/// Attempts to initialize a 2D array using the specified base offsets and lengths. 
/// The provided function can return 'None' to indicate a failure - if the initializer 
/// fails for any of the location inside the array, the construction is stopped and 
/// the function returns 'None'. 
let tryInitBased base1 base2 length1 length2 f = 
    let arr = Array2D.createBased base1 base2 length1 length2 (Unchecked.defaultof<_>) 
    /// Recursive function that fills a specified 'x' line 
    /// (returns false as soon as any call to 'f' fails, or true) 
    let rec fillY x y = 
    if y < (base2+length2) then 
     match f x y with 
     | Some v -> 
      arr.[x, y] <- v 
      fillY x (y + 1) 
     | _ -> false 
    else true 
    /// Recursive function that iterates over all 'x' positions 
    /// and calls 'fillY' to fill individual lines 
    let rec fillX x = 
    if x < (base1+length1) then 
     if fillY x base2 then fillX (x + 1) 
     else false 
    else true 
    if fillX base1 then Some arr else None 

Затем вы можете сохранить свой код почти одинаково, но замените initBased на tryInitBased и верните None или Some(res) из функции лямбда.

Я также разместил функцию до F# snippets с более приятным форматированием.

+0

Будучи неофитом F #, я все еще перевариваю красоту вашего решения. Brilliant. – prokilomer