【问题标题】:Ravi Sethi's Little Quilt Language in HaskellRavi Sethi 在 Haskell 中的小被子语言
【发布时间】:2023-03-03 00:32:01
【问题描述】:

我正在尝试在 Haskell 中实现 Ravi Sethi 的 Little Quilt 语言。 Sethi的小被子概览可以在这里看到:http://poj.org/problem?id=3201

以下是我目前拥有的功能:

import Data.List.Split

rotate :: Int -> [a] -> [a]
rotate n xs = iterate rot xs !! n 
    where 
        rot xs = last xs : init xs 

turn :: [a] -> [a]
turn x = rotate 2 x

grid :: Int -> [String] -> String
grid n = unlines . map concat . chunksOf n

printAtom :: [String] -> IO() 
printAtom x = putStrLn $ grid 2 x  

我实现了rotate 以在我的turn 函数中使用,因为它只是将列表n 向左旋转了几次。

这是一个示例原子:

let a0 = ["#", "@", "#", "#"]

为了说明如何查看原子,我将使用 printAtom 函数:

printAtom a0 

#@
## 

当我在原子 a0 上调用 turn 并打印结果原子时,我最终得到以下结果(turn 应该代表整个原子顺时针旋转 90 度):

##
#@

这是第一回合的预期输出。这将对应于定向原子a1。打开原子a1 应该会产生:

@#
## 

然而,考虑到turn 函数的约束,它只是简单地将原子返回到a0 状态。为了解决这个问题,我尝试实现一个函数newTurn,它使用基于chunksOf 2 atom 测试的警卫,如下所示:

newTurn :: [a] -> [a]
newTurn x
| chunksOf 2 x == [["#", "@"], ["#", "#"]] = rotate 2 x
| chunksOf 2 x == [["#", "#"], ["#", "@"]] = rotate 1 x 
| chunksOf 2 x == [["@", "#"], ["#", "#"]] = rotate 2 x 
| chunksOf 2 x == [["#", "#"], ["@", "#"]] = rotate 1 x 

我几乎可以肯定我不了解如何使用守卫,而且我绝对知道我不太了解函数定义上的类型约束。当我尝试将 newTurn 函数导入 ghci 时,出现此错误:

functions.hs:19:29:
Couldn't match type `a' with `[Char]'
  `a' is a rigid type variable bound by
      the type signature for newTurn :: [a] -> [a] at functions.hs:18:1
In the expression: "#"
In the expression: ["#", "@"]
In the second argument of `(==)', namely `[["#", "@"], ["#", "#"]]'

在对我的问题进行了冗长的解释之后,基本上我需要知道的是如何更改我的 turn 函数以表示原子实际顺时针旋转 90 度? (注意:这是我尝试在 Haskell 中处理的第一个项目,所以我确信我的代码相当混乱。)

【问题讨论】:

    标签: haskell list-manipulation


    【解决方案1】:

    让我们首先关注转牌。对于 [a, b, c, d] 的原子,在其上调用 grid 2 以打印产量

    a b
    c d
    

    顺时针旋转 90° 会导致

    c a
    d b
    

    来自列表[c, a, d, b]。所以顺时针转动不是列表元素的循环交换。如果只需要考虑 2×2 个原子,那么使用平面列表的 turn 的自然实现将是

    turn [a,b,c,d] = [c,a,d,b]
    turn _         = error "Not an atom"
    

    但是,根据概述,事情并没有那么简单,你可以缝制被子,所以你可以得到任何尺寸的被子m×nmn 都是偶数。因此,对被子使用平面列表表示并不是最好的主意。

    假设您将被子表示为列表列表,每行一个列表,例如

    [ [a,b,c,d]
    , [e,f,g,h] ]
    

    2×4 被子。顺时针旋转 90° 得到4×2 被子

    [ [e,a]
    , [f,b]
    , [g,c]
    , [h,d] ]
    

    现在,标准库中没有任何东西可以直接执行此操作,但是在 Data.List 中,我们有 transpose,它将上面的 2×4 被子转换为

    [ [a,e]
    , [b,f]
    , [c,g]
    , [d,h] ]
    

    然后我们就到了一半:

    turn = map reverse . transpose
    

    根据概述,在转动时,还需要旋转符号,'\' 变为 '/' 反之亦然,'-' 变为 '|' 反之亦然。这可以通过将turnChar :: Char -> Char 函数映射到所有行来实现。

    【讨论】:

    • 我无法将 turn 定义为函数。如果我说let turn = map reverse . transpose 并运行:t turn,它会给我turn :: [[a]] -> [[a]]。当我在函数定义文件中将其用作 turn 的类型签名时,出现以下错误:Couldn't match expected type [[a]] with actual type a0 -> c0
    • 如果不查看代码,我无法准确诊断,但该消息看起来好像您在某处将函数传递给 turnturn turn 之类的东西。您可以在某处发布确切的问题部分吗?
    • 采纳您的建议,turn = map reverse . transpose,当在解释器中传递列表列表时,它确实有效。以下是我尝试将其定义为函数的方法:turn :: [[a]] -> [[a]] turn xs = map reverse xs . transpose xs。这给了我之前评论中显示的类型错误。
    • 啊,不。这是错误的定义方式。您可以将其定义为无点、turn = map reverse . transpose 或带参数,但它会是turn xs = map reverse (transpose xs)。在您所拥有的中,组合运算符(.) 的第一个参数是map reverse xs - 这是一个列表列表,类型为[[a]],因此第二部分transpose xs,但(.) 需要一个函数对于它的两个论点。
    • 所以当我定义它时,我完全放弃了函数的类型签名并且它可以工作。不包含类型签名是不好的做法吗?或者它没有相应的“正确”类型签名,因为我是这样定义的?
    【解决方案2】:

    这是一个示例原子:

    ["A", "B", "C", "D"]
    

    下面是你的显示方式:

    AB
    CD
    

    这里的问题是旋转(一维)列表(从一端弹出一个元素并将其推到另一端)的自然方式不是旋转 2x2 正方形的方式。

    我建议使用不同的数据结构来表示原子。例如您可以将原子表示为列表列表:

    [["A", "B"], ["C", "D"]]
    

    【讨论】:

    • 甚至:data Quilt a = Quilt { topLeft :: a, topRight :: a, bottomLeft :: a, bottomRight :: a }
    • @dflemstr 是的,但这不会推广到他想将原子缝合在一起的时候(尽管列表的列表可能也不会),而且我累了,没有精力解释他为什么要那样做(或者甚至在某个地方找到预先写好的解释)。
    猜你喜欢
    • 2021-03-23
    • 1970-01-01
    • 1970-01-01
    • 2012-09-02
    • 1970-01-01
    • 2020-09-10
    • 1970-01-01
    • 2014-03-13
    • 1970-01-01
    相关资源
    最近更新 更多