haskell中的高阶函数谜题求解函数

时间:2018-12-03 19:15:31

标签: haskell higher-order-functions

我正在尝试用金字塔重建一个谜语:

Illustration 1

金字塔的最后一层是数字从1到n的排列,其中n是字段数。然后,不在最低层中的所有其他字段都是该数字对角线下方数字的总和。

所以我想做一个函数,当给定左边的谜语时,返回右边的解。我打算通过枚举这样的层来做到这一点:

Enumerating Pyramid

对于图层,我创建了一个自定义数据类型:

data Layer = One | Two | Three | Four | Five | Six
deriving (Eq,Ord,Enum,Show)

和其他类型:

type LayerDepth = Layer
type FieldNumber = Int
type FieldContent = Int
type FieldAssignment = (FieldNumber -> FieldContent)
type PyramidRiddle = FieldAssignment
type PyramidSolution = FieldAssignment
type PyramidSolutions = [PyramidSolution]

和功能:

solveRiddle :: LayerDepth -> PyramidRiddle -> Pyramidsolutions

对于所示示例,我将创建类型为(FieldNumber-> FieldContent)的匿名函数:

fieldAssignment1 = \n -> if (n==6) || n==8) then 3 else 0

此功能将用数字3标记第六和第八字段

然后调用: solveRiddle Four fieldAssignment1 ->> [pyramidSolution1, pyramidSolution2]

四层代表四层,而PyramidSolutions是FieldAssignments列表,具有谜题的解决方案

我的问题:

我将需要以某种方式返回一个函数,该函数将给定字段分配以计算最后一层的排列,并根据该分配将数字分配给其余字段。

这样:

pyramidSolution1 = \n -> case n of 1 -> 18
                                   2 -> 11 
                                   3 -> 7
                                   4 -> 7 
                                   5 -> 4 
                                   6 -> 3 
                                   7 -> 4 
                                   8 -> 3 
                                   9 -> 1 
                                  10 -> 2
                                   _ -> 0 

pyramidSolution2 = \n -> case n of 1 -> 20
                                   2 -> 12 
                                   3 -> 8
                                   4 -> 7 
                                   5 -> 5 
                                   6 -> 3 
                                   7 -> 4 
                                   8 -> 3 
                                   9 -> 2 
                                  10 -> 1
                                   _ -> 0 

但是最好的方法是什么?

我该如何分配数字的排列,并知道如何将它们排列为数字等于下面的数字之和?

在上面的代码中实现匿名函数pyramidSolution1和pyramidSolution2的最佳方法是什么?

1 个答案:

答案 0 :(得分:8)

我会简化一下。一层是数字列表:

type Layer = [Int]
-- e.g. [4,3,1,2]

规则是对某些元素的固定分配的列表。

data Must = Any | Only Int  -- Yes, it's just Maybe with different labels

type Rule = [Must]
-- e.g. [Any,Only 3,Any,Any]

您需要一个可以从其下一层生成图层的函数:

nextLayer :: Layer -> Layer
nextLayer = undefined
-- nextLayer [4,3,1,2] == [7,4,3]

和用于根据有效规则检查图层的功能

isLayerValid :: Rule -> Layer -> Bool
isLayerValid = undefined
-- isLayerValid [Any,Any,Only 3] [1,2,3] == True
-- isLayerValid [Any,Any,Only 3] [1,3,2] == False

一个谜语只是规则列表:

type Riddle = [Rule]
riddle :: Riddle
riddle = [[Any, Only 3, Any, Any], [Any, Any, Only 3], [Any, Any], [Any]]

一个解决方案是从某个基础开始的层列表。

type Pyramid = [Layer]
pyramid :: Layer -> Pyramid
pyramid [] = []
pyramid base = base : pyramid (nextLayer base)
-- pyramid [4,3,1,2] == [[4,3,1,2],[7,4,3],[11,7],[18]]

正确解决方案是针对给定谜语进行验证的解决方案:

isValidFor :: Riddle -> Pyramid -> Bool
isValidFor [] [] = True
isValidFor (r:rs) (x:xs) = isLayerValid r x && isValidFor rs xs
-- isValidFor riddle (pyramid [4,3,1,2]) == True
-- isValidFor riddle (pyramid [1,3,4,2]) == False

现在的诀窍是生成所有潜在的解决方案

permutations :: [Int] -> [[Int]]
permutations ns = undefined

-- e.g. allSolutions = map pyramid (permutations [1..n])

并使用解决方案测试对其进行过滤:

solutions :: Riddle -> [Pyramid]
solutions r = filter (isValidFor r) (map pyramid (permutations [1..length r]))
-- solutions riddle == [pyramid [4,3,1,2], pyramid [4,3,2,1]]