【问题标题】:Haskell - pattern match over a 2D grid using a sliding windowHaskell - 使用滑动窗口在 2D 网格上进行模式匹配
【发布时间】:2020-06-23 16:34:10
【问题描述】:

我正在尝试实现一个函数来匹配二维网格内 Haskell 中的特定模式。例如,如果我的网格定义如下:

grid = [[0,0,0,0],
        [0,1,1,0],
        [0,1,1,0],
        [0,0,0,0]] 

我正在寻找的模式是:

pattern = [[1, 1], 
           [0, 0]]

我希望它返回网格中左上角元素的索引。在这种情况下,(2,1)。我的函数有以下类型:

matches2d :: Eq a 
          => [[a]]          -- ^ The 2D grid to match over 
          -> [[a]]          -- ^ The 2D pattern to match
          -> [(Int, Int)]   -- ^ Top left corner of sections matching the pattern

我不确定如何解决这个问题。我在这里尝试使用的方法是网格上模式的滑动窗口,但我现在一直在尝试实现类似的东西。到目前为止,这是我所拥有的,但我认为我错过了它背后的一些逻辑:

matches2d :: Eq a => [[a]] -> [[a]] -> [(Int, Int)] 
matches2d _ [] = [] 
matches2d f (x:xs) = matcher x pattern 
  where matcher x y (x:xs) = sort x == sort y

提前感谢您的帮助。

【问题讨论】:

  • 有很多小问题会导致函数无法使用。请发布您对定义的尝试,以便我们有一些特定的价值。现在,你的问题太宽泛了。
  • 请向我们展示您的一维匹配尝试。你说你可以做一排。显示出来。
  • 您还没有向我们展示任何有意义的代码。请向我们展示您暗示可以执行的一维代码。我问这个的原因是因为否则我们不知道是什么阻止你完成任务。我们可以为您发布一些代码,但如果它从您当前水平之上开始,您会在那里理解什么?即使你这样做了,你会从中学到什么?如果你看到zipWith (zipWith (==) ...) ... 之类的东西怎么办,这样的代码对你来说是清楚的吗?它本身就足够暗示了吗?请回复,否则,我们可能会断定您对这个话题失去了兴趣。
  • 您的问题还可以重新打开,但我的猜测如果我们从您那里得到一些反馈,它更有可能发生。
  • 好吧,既然你已经弄清楚了,你现在可以发布你自己的答案,然后接受它!你也可以获得一些声誉。它现在重新打开,所以你可以这样做。您不必发布整个代码,以防它用于分配;我想,一些亮点和解释就可以了。干杯。

标签: haskell pattern-matching sliding-window


【解决方案1】:

用递归解决它。

这是一些迭代开发。在下文中,您首先需要了解列表推导和 zipWith 函数。它是解方程的最简单的函数

zipWith <b>f</b> (x:xs) (y:ys) = <b>f</b> x y : zipWith <b>f</b> xs ys

或者,如果您熟悉zip,

zipWith <b>f</b> xs ys = [<b>f</b> x y | (x,y) &lt;- zip xs ys]

还有,

and (x:xs) = x && and xs
           =  all (\x -> x == True) xs
           =  all (\x -> x) xs
           =  all id xs

这样,

1.

matches2d_1 :: Eq a => [[a]] -> [[a]] -> Bool
matches2d_1 pattern xss = 
    and (map and (zipWith (zipWith (==)) pattern xss))     -- does it match?
    ||  matches2d_1 pattern (drop 1 xss)            -- or start at next row
    ||  matches2d_1 pattern (map (drop 1) xss)      -- or start at next column

这在极端情况下是错误的,但给出了一个总体思路。它的核心是and (zipWith (\a b -&gt; and (zipWith (==) a b)) pattern xss) 位。

请注意,它只为我们提供了第一个成功结果的指示,但我们想要所有这些结果,因此,一个列表。因此,

2.

matches2d_2 :: Eq a => [[a]] -> [[a]] -> [Bool]
matches2d_2 pattern xss    | shorter xss pattern       = []  -- not enough rows
matches2d_2 pattern (xs:_) | shorter xs (head pattern) = []  -- not enough columns
matches2d_2 pattern xss = 
    [True | and (map and (zipWith (zipWith (==)) pattern xss))]  -- this match
    ++  matches2d_2 pattern (drop 1 xss)            -- or other
    ++  matches2d_2 pattern (map (drop 1) xss)      --      ones...
shorter a b = -- length a < length b
       .... implement it better

这会处理边缘情况,但不会返回实际成功 结果它仍然只报告每个结果。

3.

matches2d :: Eq a => [[a]] -> [[a]] -> [(Int,Int)]
.....

对以上内容进行扩充应该可以得到我们想要的实际结果。

4.

按照规范进行工作实施,接下来我们可以考虑效率。它会重复做任何多余的工作吗?这可以避免吗?

【讨论】:

  • 好消息,您已经跟进,甚至完成了! (许多新手提问者没有)。干杯,不客气。 :)
【解决方案2】:

所以解决这个问题花了我一些时间。我需要定义另一个辅助函数和其他一些函数来让它工作。归结起来,有一个slicer 函数:它采用一个完整的网格和两个元组,然后提取子网格,其中行在第一个元组的范围内,第二个元组的列。它的定义如下:

slicer :: [[a]] -> (Int, Int) -> (Int, Int) -> [[a]]
slicer [] _ _ = []
slicer a (x1,x2) (y1,y2) =form (head ( [[index a (i,j)|i<-[x1..x2-1],j<-[y1..y2-1]]])) (x2-x1,y2-y1)

我还需要一个函数来返回网格中给定索引处的值,index:

index :: [[a]] -> (Int, Int) -> a
index (b:bb) (0,0) = head b
index (c:cc) (0,y) = index [(tail c)] (0,y-1)
index (d:dd) (x,y) = index dd (x-1,y)

接下来是定义helper 函数。这花费了该函数的大部分工作。它有助于将操作“嵌套”在整个网格上。它的整个功能是在整个网格上滑动固定大小的窗口并找到匹配项。一旦完成,它会给出该匹配窗口最左上角的坐标。它的定义是这样的:

helper :: Eq a => [[a]] -> [[a]] -> (Int, Int, Int) -> [(Int, Int)]
helper x _ (l, m, n) | (l > length x) = []
helper x y (l, m, n) | (n > length (concat (take 1 x))) = helper x y (l+1, 0, (length (concat (take 1 y))))
helper x y (l, m, n) | (slicer x (l, (length y) + l) (m, n) == y) = [(l, m)] ++ helper x y (l, (m+1), (n+1))
helper x y (l, m, n) | (slicer x (l, (length y) + l) (m, n) /= y) = [] ++ helper x y (l, (m+1), (n+1))

最后,在所有这些子功能之后,是时候真正制作我从一开始就尝试制作的功能了。具有讽刺意味的是,它是其中最简单的,因为它只在网格长度上采用给定的模式。

matcher :: Eq a => [[a]] -> [[a]] -> [(Int, Int)]
matcher x y = helper x y (0, 0, (length (concat (take 1 y))))

【讨论】:

  • form 在slicer 中使用的是什么?
  • 忘了包括那个。这是另一个功能,仅用于从我提供的列表中实际制作一个网格。只需要一个列表和一个 (height, width) 的元组并返回该形状的网格。
猜你喜欢
  • 2021-01-22
  • 1970-01-01
  • 1970-01-01
  • 2016-05-17
  • 1970-01-01
  • 1970-01-01
  • 2013-07-31
  • 2022-11-27
  • 1970-01-01
相关资源
最近更新 更多