Альтернативный подход, чтобы избежать предупреждения "Неполное сопоставление с образцом"

Я написал функцию, которая принимает массив в качестве входных данных и возвращает массив равного размера в качестве выходных данных. Например:

myFunc [| "apple"; "orange"; "banana" |]
> val it : (string * string) [] =
    [|("red", "sphere"); ("orange", "sphere"); ("yellow", "oblong")|]

Теперь я хочу присвоить результаты через привязку let. Например:

let [|
        ( appleColor, appleShape );
        ( orangeColor, orangeShape );
        ( bananaColor, bananaShape )
    |] = 
    myFunc [| "apple"; "orange"; "banana" |]

Который прекрасно работает...

> val orangeShape : string = "sphere"
> val orangeColor : string = "orange"
> val bananaShape : string = "oblong"
> val bananaColor : string = "yellow"
> val appleShape : string = "sphere"
> val appleColor : string = "red"

... кроме того, что выдает предупреждение:

warning FS0025: Incomplete pattern matches on this expression. For example, the value '[|_; _; _; _|]' may indicate a case not covered by the pattern(s).

Источник и причина предупреждения уже были раскрыты, я просто ищу краткий обходной путь. Этот вызов функции происходит в верхней части моей функции, и мне не нравится идея помещать все тело функции в соответствие:

let otherFunc =
    match myFunc [| "apple"; "orange"; "banana" |] with
    | [|
        ( appleColor, appleShape );
        ( orangeColor, orangeShape );
        ( bananaColor, bananaShape )
      |] ->
        // ... the rest of my function logic
    | _ -> failwith "Something impossible just happened!"

Это просто плохо пахнет. Мне тоже не нравится игнорировать предупреждение - это противоречит моему лучшему суждению. Есть ли другие варианты, открытые для меня, или мне просто нужно найти другой подход полностью?

2 ответа

Решение

Одна из возможностей, если вы ожидаете, что такой тип вызова будет частым, - это создать обертки, которые будут работать с ожидаемыми размерами кортежей, например

myFunc3 (in1,in2,in3) =
    match myFunc [|in1;in2;in3|] with
    [|out1;out2;out3|] -> out1, out2, out3
    _ -> failwith "Internal error"

и т. д. Но все, что он делает, это перемещает некрасивый код в стандартное место, и выписывать обертки будет неудобно.

Я не думаю, что есть лучший вариант с этим API, потому что нет никакого способа сказать компилятору, что myFunc всегда возвращает одинаковое количество элементов, которые ему переданы.

Другой вариант может быть заменить myFunc с IDisposable учебный класс:

type MyClass() =
   let expensiveResource = ...


   member this.MyFunc(v) = ...calculate something with v using expensiveResource

   interface IDisposable with
       override this.Dispose() = // cleanup resource

а затем использовать его в блоке, как

use myClass = new MyClass()
let appleColor, appleShape = myClass.MyFunc(apple)
...

Адаптируя ответ @Ganesh, вот примитивный способ решения проблемы:

let Tuple2Map f (u, v) 
    = (f u, f v)

let Tuple3Map f (u, v, w) 
    = (f u, f v, f w)

let Tuple4Map f (u, v, w, x) 
    = (f u, f v, f w, f x)

Пример:

let Square x = x * x
let (a,b) = Tuple2Map Square (4,6)
// Output:
// val b : int = 36 
// val a : int = 16

Но я думаю, что-то еще более примитивное было бы так:

let Square x = x * x
let (a,b) = (Square 4, Square 6)

И если имя функции слишком длинное, например

// Really wordy way to assign to (a,b)
let FunctionWithLotsOfInput w x y z = w * x * y * z
let (a,b) = 
    (FunctionWithLotsOfInput input1 input2 input3 input4A,
     FunctionWithLotsOfInput input1 input2 input3 input4B)

Мы можем определить временную функцию

let FunctionWithLotsOfInput w x y z = w * x * y * z
// Partially applied function, temporary function 
let (a,b) =
    let f = (FunctionWithLotsOfInput input1 input2 input3)
    (f input4A, f input4B)
Другие вопросы по тегам