给定一串整数和一个目标数字,打印出元组及其结果的所有可能组合

时间:2013-05-05 23:53:27

标签: haskell

嗯,我确实理解haskell(大多数)的重点是使用递归从更简单的函数构建更复杂的函数的优点,我有一个名为pair的函数,从一个int的int返回所有可能的元组组合,然后我有另一个叫做操作的函数,它有一个给定的元组,打印出两个数字可以做的所有可能的操作(*,+, - ,/),现在出现了我无法绕过我的部分:

-- Find all possible 2-combinations of the elements of xs.
pairs :: [Int] -> [(Int, Int)]
pairs xs = [(x, y) | (x:ys) <- tails xs, y <- ys]

operations :: (Int, Int) -> [(Int, Int, Char, Int)]
operations (x, y) =
    [ (x, y, '+', x + y) ] ++
    [ (x, y, '*', x * y) ] ++
    [ (x, y, '-', x - y) | x > y, (x/=4 && y/=2) ] ++
    [ (x, y, '/', x `div` y) | x >= y, x `mod` y == 0]

我正在尝试实现一个给出一串整数和一个目标数字的函数(最终目的是使用整数字符串获取该数字)我打印出元组及其结果的所有可能组合,例如)

solve ( 100 , [1,4,5] , [] )

[ ( 100 , [5,5] , [(1,4,'+',5)] ),take first tuple 1,4 add and subs into "new tuple"5,5
( 100 , [3,5] , [(4,1,'-',3)] ),
( 100 , [6,4] , [(1,5,'+',6)] ),
( 100 , [4,4] , [(5,1,'-',4)] ),
( 100 , [9,1] , [(4,5,'+',9)] ),
( 100 , [1,1] , [(5,4,'-',1)] ),
( 100 , [20,1] , [(4,5,'*',20)] ) ]

我很困惑如何处理这个,因为我知道我已经有一个功能打印一个元组上的所有可能的操作和一个产生所有元组但我无法看到如何组合它们,任何帮助将是谢谢,谢谢。


我看到你的解决方案并且有意义,但我从头开始为时已晚,

我做到了这一点:

solve(n,ns) = [ e | ns' <- pairs ns
                      , e   <- operations ns'] 

(100,[3,5],[(4,1,' - ',3)]),是我想要的

我知道,我想尝试一下工作,因为它看起来有点不同,我在第二次之后感到困惑,我在Haskell仍然有点可怕。 这就是我的功能: 对:给定一个String返回所有可能的元组:对[1,2,3,4,5,6]将返回[(1,2),(1,3)......等] 操作接受一个元组并返回所有可能的操作与该元组(必须是正整数结果,否则我们不想要它),最后

solve(n,ns) = [ e | ns' <- pairs ns
                      , e   <- operations ns'] 

取n个目标数,ns为6 +整数字符串,到目前为止返回一个字符串,其中包含所有打印元组的组合,如:[(3,'+',4,7),(3,'* * ',4,12)......等等但是我想在每个阶段打印:

[n,(result of tuple operation,string number)(tuple operation)]
eg ( 100 , [5,5] , [(1,4,'+',5)] ),take first tuple 1,4 add and subs into "new tuple"5,5
( 100 , [3,5] , [(4,1,'-',3)] ),
( 100 , [6,4] , [(1,5,'+',6)] ),

2 个答案:

答案 0 :(得分:3)

有很多方法可以解决这个问题。以下是相对简单的Haskell-esque解决方案的概述。请注意,它使用代数数据类型,因此如果您尚未熟悉,则需要熟悉它。

注意:这是一个涉及问题的问题。我的解决方案(相对干净)长55行。

第一步是为您的问题定义适当的数据类型。我将选择以下内容:

data Expr = Lit Int
   | Plus Expr Expr
   | Times Expr Expr
   | Minus Expr Expr
   | Divide Expr Expr
  deriving Show

类型Expr的值是一个表达式树,它由四个二进制运算组成,并在其叶子上有整数。使用此定义,您将需要定义以下函数:

eval :: Expr -> Int       -- "evaluate" a expression

exprs :: [Int] -> [Expr]  -- derive all expression trees whose literals come from
                          -- a list of integers

然后找到评估特定数字的表达式只是:

findexprs :: [Int] -> Int -> [Expr]
findexprs xs y = filter (\e -> eval e == y) $ exprs xs

eval

eval函数将是一个简单的案例分析:

eval (Lit x) = x
eval (Plus a b) = (eval a) + (eval b)
eval (Minus a b) = (eval a) - (eval b)
...

提示:对于除法,请查找quot函数。

exprs

exprs的前几个案例非常简单:

exprs :: [Int] -> [Expr]
exprs []  = []
exprs [x] = [ Lit x ]
exprs xs  = ...

如果列表中只有一个数字,则您可以创建的唯一表达式为Lit

exprs的最终案例如下:

  1. xs划分为两个子列表:leftright
  2. 使用列表left
  3. 制定表达式树
  4. 使用列表right
  5. 制定表达式树
  6. 将两个表达式树与二元运算符
  7. 组合在一起

    步骤2和3只是对exprs函数的递归调用。第4步只是遍历所有可能的二元运算符。您可以使用列表推导。

    对于第1步,我们需要找到将列表拆分为两个子列表的所有方法。 也就是说,我们需要定义一个函数:

    parts :: [Int] -> [ ([Int], [Int]) ]
    

    例如,parts [1,2] = [ ([1,2],[]), ([1],[2]), ([2],[1]), ([], [1,2]) ]

    当然,parts可以递归定义,诀窍是找到模式:

    parts [] = [ ([],[]) ]
    parts (x:xs) = ...???...
    

    这里的提示是问自己如何从parts (x:xs)形成parts xs

    注意事项

    我遗漏了一些实施细节。首先,如果你真的想要正确地实现除法,你可能不得不重新考虑eval的这种类型签名:

    eval :: Expr -> Int
    

    最初为了让事情有效,你可能想要省略分部操作员。那么您可能想要阅读Maybe数据类型。

    我也在exprs的定义中省略了细节。在我概述的步骤中潜伏着一个无限循环的陷阱(可以很容易地进行侧步)。

    祝你好运!

    评论

    (由于SO不喜欢评论中长期运行的主题,我会在这里解决OP的问题。)

    正如我之前提到的,有很多方法可以解决这个问题,例如:看到 Algorithm for permutations of operators and operands

    这种方法更复杂,但它是一种有用的分解模式 你会看到在Haskell中广泛使用的。它也需要分开 以下问题:

    1. 生成可能的树(exprs函数)
    2. 评估表达式树(eval函数)
    3. 找到感兴趣的树木(filter ...
    4. 您的方法结合了前两个问题。这可能不是问题 如果你只是解决这个问题,但假设你改变了标准 为了法律表达。例如,如果列表中的数字可以重复使用,该怎么办? 多次(目前数字只能使用一次。)或者,如果不这样做,那该怎么办? 需要使用所有数字?这些变化只需要改变 exprs函数。

答案 1 :(得分:1)

我想我终于明白你要做什么了。

在阅读以下代码时,您应该在整个样本列表上运行choose1pairs,以查看它们的作用,例如: choose1 [2,5,7]pairs [1,2,3]

phi将所有可能的评估作为一对(x,hs)返回,其中x是最终结果,hs是操作历史记录(列表)。请注意,历史记录是向后的 - hs列表的第一个元素是最后执行的操作。

hs列表的每个元素本身都是(Int,Char,Int,Int)形式的元组 - 例如(3,'-',4,-1)并表示操作3-4 => -1

作为测试,请尝试:head $ solve [3,7,13,19,23,29] 823

import Data.List (inits,tails)

always _ _ = True
canDivide a b = (b /= 0) && (a `mod` b) == 0

ops :: [ ( Int -> Int -> Int, Char, Int -> Int -> Bool) ]
ops = [ ((+), '+', always),
        ((-), '-', always),
        ((*), '*', always),
        (div, '/', canDivide) ]

choose1 xs = zip xs zs
  where zs = zipWith (++) (inits xs) (tail $ tails xs)

pairs xs = [ (x,y,r2) | (x,r1) <- choose1 xs, (y,r2) <- choose1 r1 ]

phi xs = go xs []
    where
      go [] hs  = []
      go [x] hs = [ (x,hs) ]
      go xs hs  = [ (x,h) |
          (a,b,rest) <- pairs xs,
          (op,name,can) <- ops,
          can a b,
          let c = op a b,
          (x,h) <- go (c:rest) ((a,name,b,c):hs) ]

solve :: [Int] -> Int -> [ (Int, [ (Int, Char, Int, Int) ] ) ]
solve xs n = filter (\(x,hs) -> (x == n)) $ phi xs