我正在编写一个SML Yahtzee / Poker程序,该程序以格式接收列表作为输入
yahtzee(1,3,3,4,3)
,其中每个数字都是掷骰子的结果,然后打印相应的结果,在这种情况下为
(three-of-a-kind, 14)
在这种情况下,最后一个数字是接收到的所有5个骰子的总和。
我目前使用字符串,并使用一个count变量对每个数字的出现进行计数,因为骰子掷骰有6种可能的结果-1、2、3、4、5或6。
我按升序对计数进行排序,以便新列表的最后一个数字为我提供有关可能是“手”的一些信息,其中包括:
yahtzee-属于5类,“价值” 50分,因此回报为(yahtzee, 50)
大笔直-5个数字序列,价值40分,因此返回(large-straight, 40)
小笔直-4个数字序列,30个点,所以(small-straight, 30)
满屋子-3分,另外2分,25分,所以(full-house, 25)
四种骰子-与所有5个骰子的总和一样多,所以(four-of-a-kind, sum)
一种类型的三个-与所有5个骰子的和一样多,所以(three-of-a-kind, sum)
机会-(chance, sum)
例如,因为我也不关心出现3次的次数,因为无论如何它都是三分之二。通过检查最后一个数字,我可以对发生的事情有一个很好的了解。
对于满屋子,如果我检查最后一个屋子之前的数字,它等于2,我知道这是满屋子,因为最后一个屋子将是3。重要的是要注意满屋子的手数大于25,但是,那只手应该是三只手,因为它会产生更多的点数。
我还没有实现所有情况,所以下面的代码是我目前拥有的,最后一个功能是尝试测试我是否朝着正确的方向前进,但是它将重命名为{{1 }},以便它可以接收输入。
yahtzee(L)
如果删除最后一个函数,则构建文件没有问题,但是,使用最后一个函数,我会收到错误消息
fun counter (nil, count1:int, count2:int, count3:int, count4:int, count5:int, count6:int) = ListMergeSort.sort (fn (x,y) => x > y) [count1, count2, count3, count4, count5, count6]
| counter (6::t, count1, count2, count3, count4, count5, count6) = counter (t, count1, count2, count3, count4, count5, count6+1)
| counter (5::t, count1, count2, count3, count4, count5, count6) = counter (t, count1, count2, count3, count4, count5+1, count6)
| counter (4::t, count1, count2, count3, count4, count5, count6) = counter (t, count1, count2, count3, count4+1, count5, count6)
| counter (3::t, count1, count2, count3, count4, count5, count6) = counter (t, count1, count2, count3+1, count4, count5, count6)
| counter (2::t, count1, count2, count3, count4, count5, count6) = counter (t, count1, count2+1, count3, count4, count5, count6)
| counter (1::t, count1, count2, count3, count4, count5, count6) = counter (t, count1+1, count2, count3, count4, count5, count6)
| counter (h::t, count1, count2, count3, count4, count5, count6) = ListMergeSort.sort (fn (x,y) => x > y) [count1, count2, count3, count4, count5, count6];
fun sum (nil) = 0
| sum (h::t) = h + sum(t)
fun anyOfAKind (nil) = 0
| anyOfAKind (L) = List.last(L)
fun fullHouse (nil) = 0
| fullHouse (L) = List.nth(L, 3)
fun testdice (nil) = []
| testdice (L) =
let
val listSum = sum(L)
val count = counter(L,0,0,0,0,0,0)
in
if fullHouse(count) = 2 then
if listSum <= 25 then print "(fullhouse, 25)"
else print "(threeofakind, " ^ Int.toString (sum) ^ ")"
end;
我的第一个问题是,为什么我会收到此错误消息?
我想我需要退货,但是不是吗?
我还有一个问题是,我的思维过程是否很好,如果没有建议,将不胜感激。
谢谢!
答案 0 :(得分:2)
有几个主要问题:
if
没有匹配的else
。那是你的语法问题。testdice
要么要生成列表,要么要打印某些内容并生成unit
。函数的子句不能具有不同的类型。nil
只是一个名称,在某种形式上它匹配所有内容。它不是数据构造函数-“空列表”构造函数为[]
。我将假定该函数实际上应该产生一对,而不是打印具有特定格式的字符串。
我还假设输入是一个列表(如您所说),而不是示例中的五元组。
(从一种形式重写为另一种形式并不难。)
首先,定义合适的数据类型:
datatype Hand = Yahtzee
| LargeStraight
| SmallStraight
| FullHouse
| Four
| Three
| Chance;
计分功能将产生一对Hand * int
。
有很多方法可以进行;我选择基于结构而不是计数来编写分类函数。
(* Utility function; 'all xs' is true if and only if none of its elements are false. *)
fun all [] = true
| all (false :: ts) = false
| all (_ :: ts) = all ts
(* These check the conditions for each type of roll. They all assume that the input is sorted. *)
fun is_yahtzee [a,b,c,d,e] = all [a = b, b = c, c = d, d = e]
| is_yahtzee _ = false;
fun is_large_straight [a,b,c,d,e] = all [b = a + 1, c = b + 1, d = c + 1, e = d + 1]
| is_large_straight _ = false;
fun is_small_straight [a,b,c,d,e] = all [b = a + 1, c = b + 1, d = c + 1, e <> d + 1]
orelse all [b <> a + 1, c = b + 1, d = c + 1, e = c + 1]
| is_small_straight _ = false;
fun is_full_house [a,b,c,d,e] = all [a = b, b = c, c <> d, d = e]
orelse all [a = b, b <> c, c = d, d = e]
| is_full_house _ = false;
fun is_four [a,b,c,d,e] = all [a = b, b = c, c = d, d <> e]
orelse all [a <> b, b = c, c = d, d = e]
| is_four _ = false;
fun is_three [a,b,c,d,e] = all [a = b, b = c, c <> d, d <> e]
orelse all [a <> b, b = c, c = d, d <> e]
orelse all [a <> b, b <> c, c = d, d = e]
| is_three _ = false;
使用此方法,您可以通过非常简单的方式将评分规则记为一个函数:
(* Assumes that the input is sorted and has five elements. *)
fun score roll =
let val value = sum roll in
if is_yahtzee roll
then (Yahtzee, 50)
else if is_large_straight roll
then (LargeStraight, 40)
else if is_small_straight roll
then (SmallStraight, 30)
else if is_full_house roll
then (if value > 25 then (Three, value) else (FullHouse, 25))
else if is_four roll
then (Four, value)
else if is_three roll
then (Three, value)
else (Chance, value)
end
答案 1 :(得分:1)
这就是我重构的方式。
为了更好地确定我的掷骰数,我想建立骰子的直方图:
fun histogram [] = []
| histogram (d::ds) =
let
fun insert x [] = [(x, 1)]
| insert x ((y,n) :: hist) =
if x = y
then (y,n+1) :: hist
else (y,n) :: insert x hist
in
insert d (histogram ds)
end
fun frequencyOf (x, []) = 0
| frequencyOf (x, (y,n)::hist) =
if x = y
then n
else frequencyOf (x, hist)
fun highestFrequency [] = 0
| highestFrequency ((x,i)::hist) =
Int.max (i, highestFrequency hist)
这样,如果直方图中只有一个元素,我就可以确定yahtzee;如果有五个元素,则可以确定为yahtzee;如果最高频率为4,则可以确定为4个。等等。
然后我想要一个自定义数据类型来表示结果的类型,
编辑:在molbdnilo之后,我还将得分与roll
类型完全分开。
datatype roll
= Yahtzee
| LargeStraight
| SmallStraight
| FullHouse
| FourOfAKind
| ThreeOfAKind
| Chance
并确定结果,
fun determineDice (d1, d2, d3, d4, d5) =
let
val dice = ListMergeSort.sort (op >) [d1, d2, d3, d4, d5]
val diceHistogram = histogram dice
in
case length diceHistogram of
1 => (Yahtzee, 50)
| 2 => if highestFrequency diceHistogram = 4
then (FourOfAKind, sum dice)
else (FullHouse, 25)
| 3 => if highestFrequency diceHistogram = 3
then (ThreeOfAKind, sum dice)
else (Chance, sum dice)
| 4 => (Chance, sum dice)
| 5 => if frequencyOf (6, dice) = 0
then (SmallStraight, 30)
else (LargeStraight, 40)
end
为打印纸卷的结果,我还将编写辅助函数:
fun showRoll Yahtzee = "Yahtzee"
| showRoll LargeStraight = "Large straight"
| ...
| showRoll (Chance _) = "Chance"
fun showRollWithPoints (roll, points) =
showRoll roll ^ " of " ^ Int.toString points ^ " points"
您可以进行其他一些改进:
determineDice
有点不稳定,因为它接受1-6范围之外的输入。您可以为骰子创建datatype die = One | Two | Three | Four | Five | Six
的自定义数据类型,也可以在Random.randInt
之上创建一个随机选择其中一个的函数,
或者您可以创建一个抽象接口,该接口仅在内部使用int
,但不允许导出非1-6的骰子。参见SML: What's the difference between using abstype and using a signature to hide the implementation of a structure?