Помогите мне объяснить функцию транспонирования F # Matrix

Существует функция Матрица транспонирования:

let rec transpose = function
    | (_::_)::_ as M -> List.map List.head M :: transpose (List.map List.tail M)
    | _ -> []

[[1; 2; 3]; [4; 5; 6]; [7; 8; 9]] |> transpose |> printfn "%A"

Он отлично работает.
Что означает (_:: _):: _?
Я не понимаю весь код!
Кто может это объяснить?
Спасибо!

Я нахожу ответ:
(_:: _):: _ - это сопоставление шаблонов по значению типа списка списков ints


Если я пишу:

let rec transpose (M:int list list) =
    match M with
    | hd::tl -> List.map List.head M :: transpose (List.map List.tail M)
    | _ -> []

Он выдает исключение во время выполнения. Что-то не так с hd?
Да, он делает что-то вроде [[], []; []] при вызове List.tail, затем он выдает исключение при вызове List.head!


Проблема решена!
Спасибо всем!

Ответы

Ответ 1

Функция не особенно читается, что может быть причиной вашей путаницы. Конструкция (_::_)::_ - это сопоставление шаблонов по значению списка типов списков int, в котором говорится, что первый случай должен выполняться при получении непустого списка непустых списков.

То же самое можно написать так. Это более подробно, но должно быть ясно, что здесь происходит:

let rec transpose matrix = 
  match matrix with   // matrix is a list<list<int>>
  | row::rows ->      // case when the list of rows is non-empty
    match row with    // rows is a list<int>
    | col::cols ->    // case when the row is non-empty
      // Take first elements from all rows of the matrix
      let first = List.map List.head matrix
      // Take remaining elements from all rows of the matrix
      // and then transpose the resulting matrix
      let rest = transpose (List.map List.tail matrix) 
      first :: rest
    | _ -> []
  | _ -> [] 

Как вы можете видеть, нам действительно не нужны значения row, rows, col и cols. Поэтому исходная реализация заменяет их на _ (которая игнорирует значение и проверяет, что список может быть разложен соответствующим образом).

В рекурсивном случае мы деконструируем матрицу следующим образом:

[ [ x; y; y ];                               [ y; y ] 
  [ x; y; y ];   =>  [ x; x; x] :: transpose [ y; y ]
  [ x; y; y ] ]                              [ y; y ] 

Я надеюсь, что картина сделает это более понятным для вас!

Ответ 3

(_::_)::_ соответствует шаблону. _ - просто неиспользуемая переменная. Это будет эквивалентно:

(a::b)::c as M -> List.map List.head M :: transpose (List.map List.tail M)

Ответ 4

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

Ответ 5

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

let rec transpose M = 
    match M with 
    | []::_ -> []
    | _ -> List.map List.head M::transpose(List.map List.tail M)