неожиданный тип возврата из списка - PullRequest
1 голос
/ 29 мая 2019

Я учу себя немного F #, занимаясь простой математикой. Я решил написать набор простых функций для объединения двух матриц, так как считал, что это будет хорошим способом изучения списков. Однако, когда я компилирую его, мои модульные тесты выдают исключение несоответствия типов.

//return a column from the matrix as a list
let getColumn(matrix: list<list<double>>, column:int) =
    [for row in matrix do yield row.Item(column)]
//return a row from the matrix as a list
let getRow(matrix: list<list<double>>, column:int) =
    matrix.Item(column)
//find the minimum width of the matrices in order to avoid index out of range exceptions
let minWidth(matrix1: list<list<double>>,matrix2: list<list<double>>) = 
    let width1 = [for row in matrix1 do yield row.Length] |> List.min
    let width2 = [for row in matrix2 do yield row.Length] |> List.min
    if width1 > width2 then width2 else width1
//find the minimum height of the matrices in order to avoid index out of range exceptions
let minHeight(matrix1: list<list<double>>,matrix2: list<list<double>>) = 
    let height1 = matrix1.Length
    let height2 = matrix2.Length
    if height1 > height2 then height2 else height1
//combine the two matrices
let concat(matrix1: list<list<double>>,matrix2: list<list<double>>) =
    let width = minWidth(matrix1, matrix2)
    let height = minHeight(matrix1, matrix2)
    [for y in 0 .. height do yield [for x in 0 .. width do yield (List.fold2 (fun acc a b -> acc + (a*b)), getRow(matrix1, y), getColumn(matrix2, x))]]

Я ожидал, что функция вернет список списков типа

double list list

Однако то, что он на самом деле возвращает, больше похоже на лямбда-выражение

((int -> int list -> int list -> int) * double list * double list) list list 

Может кто-нибудь сказать мне, что возвращается и как заставить его быть оцененным в списке списков, который я первоначально ожидал?

1 Ответ

2 голосов
/ 29 мая 2019

Есть короткий ответ и длинный ответ на ваш вопрос.

Краткий ответ

Короткая версия состоит в том, что функции F # (например, List.fold2) принимают несколько параметров не с запятыми, как вы думаете, а с пробелами между ними. То есть, вы НЕ должны называть List.fold2 так:

List.fold2 (function, list1, list2)

а вот так:

List.fold2 function list1 list2

Теперь, если вы просто удалите запятые в своем вызове List.fold2, вы увидите, что компилятор жалуется на ваш вызов getRow(matrix1, y) и говорит вам заключить их в скобки. (А внешняя пара скобок вокруг List.fold2 на самом деле не нужна). Итак, это:

(List.fold2 (fun acc a b -> acc + (a*b)), getRow(matrix1, y), getColumn(matrix2, x))

Нужно превратить в это:

List.fold2 (fun acc a b -> acc + (a*b)) (getRow(matrix1, y)) (getColumn(matrix2, x))

Длинный ответ

Способ, которым функции F # принимают несколько параметров, на самом деле сильно отличается от большинства других языков, таких как C #. Фактически, все функции F # принимают ровно один параметр! «Но подождите, - вы, вероятно, думаете прямо сейчас, - вы только сейчас показали мне синтаксис для функций F #, принимающих несколько параметров!» Да, я сделал. Под капотом происходит комбинация карри и частичного применения . Я бы написал длинное объяснение, но Скотт Влашин уже написал одно, это гораздо лучше, чем я мог бы написать, поэтому я просто укажу вам серию https://fsharpforfunandprofit.com/series/thinking-functionally.html, чтобы помочь вам понять, что здесь происходит. (Разделы о карри и частичном применении - те, которые вам нужны, но я бы рекомендовал прочитать серию по порядку, потому что более поздние части основаны на концепциях, представленных в предыдущих частях).

И да, этот «длинный» ответ кажется короче, чем «короткий», но если вы прочитаете эту серию (а затем и остальную часть отличного сайта Скотта Влашина), вы обнаружите, что он намного длиннее короткого ответ. : -)

Если у вас есть еще вопросы, я с удовольствием постараюсь объяснить.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...