F#Seq.head&;Seq.tail类型与自定义类型不匹配
我有自定义类型的F#Seq.head&;Seq.tail类型与自定义类型不匹配,f#,type-mismatch,custom-type,F#,Type Mismatch,Custom Type,我有自定义类型的等级,和套装,组合成一张自定义类型的卡,以及一个自定义类型集合手,这是一系列卡 当我在递归handValue函数中使用Seq.head时,我得到错误: 此表达式应具有类型'Card',但此处具有类型'a->'b' 我的印象是,由于手是卡的序列,因此Seq.head将返回序列中的第一个元素,即类型卡 另外,当我使用Seq.tail时,我会得到错误: 您正在传递cardValue函数的seq.head类型'a->seq;您需要先应用该函数,然后传递结果: type Suit = Sp
等级
,和套装
,组合成一张自定义类型的卡
,以及一个自定义类型集合手
,这是一系列卡
当我在递归handValue
函数中使用Seq.head
时,我得到错误:
此表达式应具有类型'Card'
,但此处具有类型'a->'b'
我的印象是,由于手
是卡
的序列,因此Seq.head
将返回序列中的第一个元素,即类型卡
另外,当我使用Seq.tail
时,我会得到错误:
您正在传递
cardValue
函数的seq.head
类型'a->seq;您需要先应用该函数,然后传递结果:
type Suit = Spades | Clubs | Hearts | Diamonds
type Rank = Ace | Two | Three | Four | Five | Six | Seven | Eight | Nine | Ten | Jack | Queen | King
type Card = {suit: Suit; rank: Rank}
type Hand = Card seq
let AllSuits = [ Spades; Clubs; Hearts; Diamonds ]
let AllRanks = [ Ace; Two; Three; Four; Five; Six; Seven; Eight; Nine; Ten; Jack; Queen; King ]
let cardValue (card:Card) =
match card.rank with
| Ace -> 1
| Two -> 2
| Three -> 3
| Four -> 4
| Five -> 5
| Six -> 6
| Seven -> 7
| Eight -> 8
| Nine -> 9
| Ten | Jack | Queen | King -> 10
let rec handValue (hand:Hand) =
if Seq.length hand = 1
then cardValue Seq.head
else cardValue Seq.head + handValue Seq.tail
注意:如果传递了一个空序列并且不是尾部递归的,则此操作将失败;修复这些问题,再加上一点冗余消除,会产生如下结果:
let rec handValue (hand:Hand) =
if Seq.length hand = 1
then cardValue (Seq.head hand)
else cardValue (Seq.head hand) + handValue (Seq.tail hand)
也就是说,这是非常低效的;当使用列表
而不是seq
时,此代码结构可以很好地工作,但最好使用高阶函数来处理后者:
let handValue hand =
let rec impl acc h =
if Seq.isEmpty h then acc
else impl (cardValue (Seq.head h) + acc) (Seq.tail h)
impl 0 hand
为什么Seq.tail效率低下
伊尔贾恩的回答是正确的;我不会重复他说的话,但我会更深入地谈一个细节。他说“这是极其低效的”,但没有详细说明。但是我将告诉您为什么应该避免反复调用Seq.tail
来迭代序列
重复调用Seq.tail
效率极低的原因在于它的实现方式。当您说let s2=Seq.tail s1
时,它会为s2
创建一个新的枚举器,该枚举器在枚举时将枚举s1
,删除其第一项,然后生成其余项。然后,再次执行递归函数,并执行与让s3=Seq.tail s2
等效的操作。这将为s3
创建一个新的枚举器,该枚举器在合并时将枚举s2
,删除其第一项,然后生成其余项。当您枚举s2
时,它将枚举所有s1
,删除其第一项,并生成其余项。结果是,s3
将删除s1
的前两项,但它会创建并枚举两个枚举器。然后在下一次循环中,让s4=Seq.tail s3
生成一个枚举器,该枚举器将枚举s3
并删除其第一项,这将导致枚举s2
并删除其第一项,这将导致枚举s1
并删除其第一项,因此,您已经创建并枚举了三个枚举
结果是,在长度为N的序列上重复调用Seq.tail
,最终创建并枚举1+2+3+4+5+…+N个枚举数,时间为O(N^2),并使用O(N^2)空间(!)。将seq转换为列表要高效得多List.tail
在时间上是O(1)并且不使用任何空间,因此通过List.tail
枚举整个列表是O(N),并且除了最初创建列表时使用的O(N)空间之外,不使用额外的空间
- 通过
和seq.head
枚举序列:O(N^2)时间和O(N^2)空间seq.tail
- 通过
和list.head
枚举列表:O(N)时间和常量空间(如果您已经拥有该列表)或O(N)空间(如果您必须从seq创建列表以枚举它)list.tail
handValue
函数。当我看到它时,我看到它所做的是:对于手中的每一张卡,它计算卡的价值,并将所有卡的价值相加得到手的价值。你知道这听起来像什么吗?这个因此,您可以像这样重写handValue
函数:
let handValue hand = hand |> Seq.sumBy cardValue
由于Seq.sumBy
是O(N)并且不需要额外的空间,因此这比将Seq转换为列表并枚举列表更有效
let handValue (hand:Hand) = hand |> Seq.sumBy cardValue