2010-06-10 43 views
14

有一个矩阵转置功能:帮我解释一下F#矩阵转置功能

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" 

它工作正常。
(_ :: _):: _是什么意思?
我不明白整个代码!
谁能解释一下?
谢谢!

我找到了答案:
(_ :: _):: _是一个图案类型的值匹配整数列表的列表


如果我写:

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


问题解决了!
谢谢大家!

回答

23

函数不是特别易读,这可能是您混淆的原因。构造(_::_)::_是与类型的值匹配的模式的列表清单的列表,其说明当得到非空列表的非空列表时应该运行第一种情况。

同样的事情可以这样写。这是更详细的,但应该清楚是怎么回事:

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 
    | _ -> [] 
    | _ -> [] 

正如你所看到的,我们并不真正需要的价值rowrowscolcols。这就是为什么原始实现用_(忽略该值并仅检查列表是否可以按要求的方式分解)替换它们。

在递归的情况下,我们解构矩阵是这样的:

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

我希望的画面让您用起来更加清晰!

+0

+1,很好的答案! – gradbot 2010-06-10 19:30:48

3

(_::_)::_是模式匹配。 _只是一个未使用的变量。这相当于:

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

这将head映射到列表上以提取第一列并使用它来形成第一行作为转置剩余列的结果的前面的行。

1

对不起碰撞过时的线程,但您的原始答案几乎是正确的。唯一的错误是空列表的终止条件。代码应该看起来像这样:

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