haskell 如何打印带有棋子位置列表的棋盘

d6kp6zgx  于 2022-11-14  发布在  其他
关注(0)|答案(2)|浏览(149)

我在做作业,我必须写一个有2个[String]的函数。列表字符串包含3个字符:

  • 第一个是国际象棋子(例如K-王,Q-后,R-车,B-象,N-骑士,P-兵)
  • 第二个指定列('a' - 'h')
  • 3rd是行号(“1”-“8”)

第一个字符串列表用于黑色棋子,第二个字符串列表用于白色棋子。棋盘的自由区域用.“”表示。黑色棋子将是大写的,白色棋子将是小写的。
不需要在板上打印1-8和a-h。
这是必需的函数类型:

chess :: [String] -> [String] -> [String]

我们有这个打印功能

pp :: Result -> IO ()
pp x = putStr (concat (map (++"\n") x))

这是IO示例:

Prelude> pp( chess["Ke1","Ra1","Rh1","Pa2","Be5"] ["Ke8","Ra8","Rh8","Pa7","Qd8","Bc8","Nb8"])
8rnbqk..r
7p.......
6........
5....B...
4........
3........
2P.......
1R...K..R
 abcdefgh

我尝试过什么:例如

chess :: [String] -> [String] -> [String]
chess _ [] = []
chess [] _ = []
chess ((x1:x2:x3:_):xs) ((y1:y2:y3:y3s):ys) 
    | 'a' == x2 && '1' == x3 = [x1] : chess xs ys
    | 'a' == y2 && '1' == y3 = [y1] : chess xs ys
    | 'b' == x2 && '1' == x3 = [x1] : chess xs ys
    | 'c' == x2 && '1' == x3 = [x1] : chess xs ys
    | 'd' == x2 && '1' == x3 = [x1] : chess xs ys
    | 'e' == x2 && '1' == x3 = [x1] : chess xs ys
    | 'e' == x2 && '1' == x3 = [x1] : chess xs ys
    | 'g' == x2 && '1' == x3 = [x1] : chess xs ys
    | otherwise = ['.'] : chess xs ys

输入为:国际象棋[“Ke 1”,“Ra 1”,“Rh 1”,“Pa 2”,“Be 1”] [“Kb 1”,“Ra 8”,“Rh 8”,“Pa 7”,“Qd 8”,“Bc 8”,“Na 1”]输出为:[“K”、“R”、“.”、“.”、“B”]
再来一次

chess :: [String] -> [String] -> [String]
chess _ [] = []
chess [] _ = []
chess ((x1:x2:x3:_):xs) ((y1:y2:y3:y3s):ys) 
    | (x2 == 'a' && x3 == ['1']) = [x1] : chess (xs) (ys)
    | (x2 == 'a' && x3 == ['2']) = [x1] : chess (xs) (ys)
    | (x2 == 'a' && x3 == ['3']) = [x1] : chess (xs) (ys)
    | (x2 == 'a' && x3 == ['4']) = [x1] : chess (xs) (ys)
    | (x2 == 'a' && x3 == ['5']) = [x1] : chess (xs) (ys)
    | (x2 == 'a' && x3 == ['6']) = [x1] : chess (xs) (ys)
    | (x2 == 'a' && x3 == ['7']) = [x1] : chess (xs) (ys)
    | (x2 == 'a' && x3 == ['8']) = [x1] : chess (xs) (ys)
    | otherwise =  chess (xs) (ys)

输入:象棋[“Ke 1”,“Ra 1”,“Rh 1”,“Pa 2”,“Be 1”] [“Ke 8”,“Ra 8”,“Rh 8”,“Pa 7”,“Qd 8”,“Bc 8”,“Nb 8”]输出:K R R
我试着先检查一行,然后再检查每一列(a-h)-我认为这是正确的,因为我需要打印类似[“K...Q...",“P......."]的内容-每个元素都是一行。如果我检查例如黑色,如果没有黑色,仍然可以有白色,所以我需要在打印'.'之前检查第二个字符串是否有白色字符。请帮助,我很困惑。我试着编写了4个函数,但它们花了4个多小时。谢谢

rdrgkggo

rdrgkggo1#

前言。
我会给予你一个概述,并允许我自己留下一些细节未经润色。请调整我的建议,以您的喜好。
我的回答将是这样的:
1.解释一下这个想法。
1.决定策略。
1.拔模类型。
1.填写定义。
在真实的生活中,我的过程是“辩证的”,所有这些思路通过反复试验同时发展。

创意。

我在想,给定两个场,每个场都有一些片断,我总是可以把这些场“放在彼此的上面”,这样每个片断在接收到的场中的位置就和它在给定场中的位置一样。(除非在同一个地方有两个片段,在这种情况下,行为是未定义的。)* 一旦我可以用这种方式添加两个字段,我可以添加任意数目的幺半群。用一个幺半群生成一个域应该不是很难。这种技巧被称为“折叠幺半群”,你会在Haskell中看到它的大量应用。

战略。

这就是我将如何解决这个问题:

  • 定义一个getPiece函数来读取一个片段。
  • 定义一个putPiece函数来显示一个字段。
  • 定义一个overlay函数,该函数覆盖任意两个字段。
  • 使用此功能折叠列表中的棋子。

类型。

type Piece = (Char, Int, Int)  -- Piece, row, column.

type Field = [String]  -- Rows.

getPiece :: String -> Piece

putPiece :: Piece -> Field

overlay :: Field -> Field -> Field

chess :: [String] -> [String] -> Field

花点时间在一张纸上,画出这些类型和功能可能如何连接的图,可能是值得的。

定义

getPiece :: String -> Piece
getPiece [v, x, y] = (piece, row, column)
  where
    piece  = v
    row    = (Char.ord y - 48)
    column = (Char.ord x - 96)

putPiece :: Piece -> Field
putPiece (v, x, y) = reverse
                   $ replaceAt (x - 1) (replaceAt (y - 1) v blank) (replicate 8 blank)
  where
    blank = replicate 8 ' '

    replaceAt :: Int -> a -> [a] -> [a]
    replaceAt i y xs =
      let (before, (_: after)) = List.splitAt i xs
      in  before ++ y: after

overlay :: Field -> Field -> Field
overlay u v = zipWith (zipWith overlayOne) u v
  where
    overlayOne ' ' y = y
    overlayOne  x  _ = x

chess :: [String] -> [String] -> Field
chess white black = List.foldl1' overlay . fmap putPiece $ pieces
  where
    pieces = fmap (makeWhite . getPiece) white ++ fmap getPiece black

    makeWhite :: Piece -> Piece
    makeWhite (c, x, y) = (Char.toLower c, x, y)

这里需要注意的是,如何将两个zipWith函数组合起来,以实现对列表列表的压缩效果。还要注意,我毫不犹豫地定义了一个helper函数replaceAt,因为我认为它会使main函数变得非常简单。

结论。

我发现用正确的抽象工具箱来处理一个简单的问题是最舒服的。在这个例子中,我们使用了一个幺半群 (由overlay定义) 和一个递归模式 List.foldl'是“catamorphism”的一个示例)。我相信你在编程实践中会遇到更多的例子,这些想法可以被用到。
如果有什么东西不平易近人或解释得不好,请给我留下评论。
好好享受 haskell 吧!
“附言” 另请参阅algorithmically faster approach,以了解非常类似的问题。

shyt4zoc

shyt4zoc2#

请考虑以下列方式组织您的程式码:

-- given the three characters you describe, figure out the position and
-- piece name (do whatever you want with nonsense)
--
-- e.g. decode "Ke8" would return ((5, 8), 'k')
--      decode "Kz92" and decode "f" can return anything convenient
decode :: String -> ((Int, Int), Char)
decode = undefined

-- given a key, find all the values in a list associated with that key
--
-- e.g. assoc (5, 8) [((3, 4), 'q'), ((5, 8), 'k'), ((1, 2), 'p')]
-- would return ['k']
--
-- in non-homeworks, don't use this; instead, use the pre-existing
-- Prelude.lookup or the containers' package's Data.Map
assoc :: Eq k => k -> [(k, v)] -> [v]
assoc = undefined

-- if there's a single good choice for character at a given board position,
-- return it; otherwise, use a . to mark an empty space
--
-- e.g. choose ['k']      would return 'k'
--      choose []         would return '.'
--      choose ['k', 'P'] can return anything convenient
choose :: String -> Char
choose = undefined

chess :: [String] -> [String] -> [String]
chess white_ black_ = [[undefined | col <- [1..8]] | row <- [8,7..1]] where
    white = [(pos, toUpper piece) | (pos, piece) <- decode white_]
    black = [(pos, toLower piece) | (pos, piece) <- decode black_]

相关问题