好吧所以我试图在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完全不熟悉,这是我尝试过的第一个真正的程序。任何帮助将不胜感激。
答案 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]]
问题是if
和else
子句的类型不同。
else
返回[[Int]]
;以及if
和IO
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