递归回溯Haskell

时间:2012-11-29 03:16:41

标签: haskell

好吧所以我试图在Haskell中创建一个Sudoku Solver,但是我收到一个错误,说我无法将预期类型[[Int]]与实际类型IO()匹配。这是我对递归求解器,错误消息和其他相关代码片段的尝试:

递归求解器尝试:

test i j q s_board = if ((valid_row i q s_board )&&(valid_column j q s_board)&&  (valid_sub_board i j q s_board)) then (solve (set_value i j q s_board)) else s_board

foo i j s_board = if ((get_value i j s_board) == 0) then [test i j q s_board | q <- [1..9]] else s_board  

solve s_board = if (full_board s_board) then (print_board s_board) else [foo i j s_board | i <- [0..8], j <- [0..8]]

如果我包含有效列,行等函数的所有定义,这个问题会非常长,但我已经检查过以确保这些工作正常。使用此代码我收到以下错误消息:

 Couldn't match expected type `[[Int]]' with actual type `IO ()'
 In the return type of a call of `print_board'
 In the expression: (print_board s_board)
 In the expression:
   if (full_board s_board) then
       (print_board s_board)
   else
       [foo i j s_board | i <- [0 .. 8], j <- [0 .. 8]]

此处还有我用来打印我的电路板的代码:

 -- showLine: this function provides formating for a single row 
showLine :: [Int] -> String
showLine = intercalate " | "
         . map unwords
         . chunksOf 3
         . map show

-- showBoad: this function provides formating for the entire board       
showBoard :: [[Int]] -> String
showBoard = intercalate "---------------------\n"
          . map unlines
          . chunksOf 3
          . map showLine


-- print_board: this function is meant to print out the entire board 
print_board :: [[Int]] -> IO ()   
print_board s_board = putStrLn $ showBoard s_board

你们看到我到目前为止的问题是什么?我对Haskell完全不熟悉,这是我尝试过的第一个真正的程序。任何帮助将不胜感激。

2 个答案:

答案 0 :(得分:7)

if的两个分支必须具有相同的类型,但在

if (full_board s_board) then
    (print_board s_board)
else
    [foo i j s_board | i <- [0 .. 8], j <- [0 .. 8]]

True分支具有类型IO(),而False分支是事物列表(在这种情况下可能是板),因此类型为[a],如果{ {1}}。

你应该分开关注点,递归回溯应该给你一个(完整)板的列表,打印应该从另一个调用foo :: Int -> Int -> [[Int]] -> a的上下文完成。

我的建议将是

solve

所以这些函数中的每一个都返回type Grid = [[Int]] solve :: Grid -> [Grid] solve s_board = if full_board s_board then [s_board] -- full means no branches, singleton list else concat [foo i j s_board | i <- [0 .. 8], j <- [0 .. 8]] test :: Int -> Int -> Int -> Grid -> [Grid] test i j q s_board | valid_row i q s_board && valid_column j q s_board && valid_sub_board i j q s_board = solve $ set_value i j q s_board | otherwise = [] foo :: Int -> Int -> Grid -> [Grid] foo i j s_board | get_value i j s_board == 0 = concat [test i j q s_board | q <- [1 .. 9]] | otherwise = [] 的列表,并通过返回可从当前网格到达的空解决方案列表来修剪死端。如果没有死亡但尚未确定,则尝试所有允许的组合。

然后你可以

Grid

但是,这会产生多次相同的解决方案,并且对于详尽的搜索而言非常效率低,因为将在所有可能的订单中进行猜测的组合。

所以,让我们来看看如何让它更有效率。如果我们有一个算法来选择我们猜测值的下一个位置,我们可以修剪结果列表中解决方案的排列和重复。最简单的算法是选择第一个空闲单元。所以让我们编写一个函数来查找网格的空闲单元格。

solve_and_print :: Grid -> IO ()
solve_and_print s_board = case solve s_board of
                            [] -> putStrLn "Sorry, that board had no solution."
                            (g:_) -> print_board g

顺便说一下,我们还测试网格是否已满,free_cells :: Grid -> [(Int,Int)] free_cells s_board = [(i,j) | i <- [0 .. 8], j <- [0 .. 8], get_value i j s_board == 0] 。所以我们可以开始解决问题了

full_board = null . free_cells

接下来,我们会找到可能放在单元格solve :: Grid -> [Grid] solve s_board | null frees = [s_board] | otherwise = guesses s_board (head frees) where frees = free_cells s_board 中的值,

(i,j)

并将它们放在单元格中并继续进行

possible_values :: Grid -> (Int, Int) -> [Int]
possible_values s_board (r,c) = [q | q <- [1 .. 9], isPossible s_board q]
  where
    isPossible v = valid_row r v s_board
                   && valid_column c v s_board
                   && valid_sub_board r c v s_board

答案 1 :(得分:4)

如果您是Haskell的新手,在处理 Monads 之前花点时间是个好主意。 IO需要monad。

首先在没有的情况下使用IO monad(putStrLn等)首先获得一个工作程序是个好主意。只需在ghci中加载您的程序,然后只需调用您的函数即可。当你对此感到满意,并且你在REPL上有一个函数可以给你答案时,你可以考虑将它打印到STDOUT(与'world'交互 - Haskell要求对monad有一些了解)。

所以请将solve功能作为初学者:

solve做到这一点 - 而不是更多。所以不要在这里做IO,比如:

solve s_board = 
  if (full_board s_board) then (print_board s_board) 
  else [foo i j s_board | i <- [0..8], j <- [0..8]]

问题是ifelse子句的类型不同。 else返回[[Int]];以及ifIO monad(print_board的结果 是不是 [[Int]]

将其更改为:

-- sometimes it helps to be explicit - solve takes a puzzle, returns a solved one.
solve :: [[Int]] -> [[Int]]  
solve s_board = 
  if (full_board s_board) then s_board                -- type of s_board is [[Int]]
  else [foo i j s_board | i <- [0..8], j <- [0..8]]   -- and so is this

只需返回结果即可。然后你在solve内弄乱ghci,继续修复你的程序并重新加载它直到它工作,一旦它完成,只需编写一个函数来调用所需的参数求解并打印它。 / p>

因此,一旦你的计划真正发挥作用,你就可以解决IO的分心:

print_board s_board = putStrLn $ show $ solve s_board

然后这将成为阅读的下一步:http://www.haskell.org/tutorial/io.html