【问题标题】:Loop a function 10 times in Haskell在 Haskell 中循环一个函数 10 次
【发布时间】:2021-07-07 07:12:12
【问题描述】:

你知道我如何将函数 func2 循环 10 次

type Vertex = Int
type OutNeighbors = [Vertex]
data Graph = Graph [(Vertex,OutNeighbors)] deriving (Eq, Show, Read)

func2 (Graph g) = filter (\x -> contains (fst x) (func1 (Graph g))) g   --I need to repeat this function 10 times.

我是haskell的新手,我不知道如何做循环

【问题讨论】:

  • 你试过写递归函数吗?
  • func2_10 = foldr1 (.) . replicate 10 (Graph . func2).

标签: function loops haskell


【解决方案1】:

你知道我如何将函数 func2 循环 10 次

您可以在 10 点 iterate!!

> take 5 $ iterate ("hi " ++) "there!"
["there!","hi there!","hi hi there!","hi hi hi there!","hi hi hi hi there!"]

> let func2 = (+3) in iterate func2 0 !! 10
30

但这需要func2 返回与其输入相同的类型,现在它似乎具有类型

func2 :: Graph -> [(Vertex,OutNeighbors)]

但如果你将Graph 包装回它,即,

func2 :: Graph -> Graph
func2 (Graph g) = Graph (... g)

那你可以iterate就可以了。

【讨论】:

  • func2 :: Graph -> Graph 这会导致错误(无法匹配预期类型Graph' with actual type [(Vertex, Types.OutNeighbors)]')
  • @karmo20 您还必须在func2 中的=filter 之间插入Graph 构造函数,并在filter ... g 部分周围添加括号。看我的回答。
【解决方案2】:

在 Haskell 中,您可以对循环使用递归,这是一个示例:

myLoop 0 g = g
myLoop n g = myLoop (n - 1) (Graph (func2 g))

现在调用myLoop 10 g 将在g 上调用func2 10 次。

请注意,我必须将结果包装回 Graph 类型,这可能是您应该在 func2 函数中执行的操作:

func2 (Graph g) = Graph (filter (\x -> contains (fst x) (func1 (Graph g))) g)

如果您将其封装在来自 transformers 包的 State monad 中,您可以获得更高级别:

import Control.Monad.Trans.State.Lazy (execState, modify)
import Control.Monad (replicateM_)

myLoop :: Int -> Graph -> Graph
myLoop n g = execState (replicateM_ n (modify func2)) g

【讨论】:

    【解决方案3】:

    这是其中一种情况,为了避免输入错误,您需要能够同时通过专有名称引用整个参数及其子组件。

    幸运的是,Haskell 提供了这一点。这被称为“as 模式”。更多细节在这里:SO-q30326249

    在您的情况下,您可以将图形参数记为:g@(Graph(pairs))。那么g就是你的graph对象,pairs就是[(Vertex,OutNeighbors)]类型对应的列表。

    你没有告诉我们你的contains函数,但可以推断它的类型是:

    contains :: Vertex -> Graph -> Bool
    

    考虑到这一点,您的图形函数的版本可以采用任意迭代计数:

    type Vertex = Int
    type OutNeighbors = [Vertex]
    data Graph = Graph [(Vertex,OutNeighbors)]  deriving  (Eq, Show, Read)
    
    funcN :: Int -> Graph -> Graph
    funcN  iterCount  g@(Graph(pairs)) =
        if (iterCount <= 0)  then  g  -- nothing to do
                             else  let
                                       gm1 = funcN (iterCount - 1) g     -- recursion
                                       fn  = \(v,ngs) -> contains v gm1  -- filtration
                                   in
                                       Graph (filter fn pairs)
    

    使用相同的技术,contains 函数的暂定版本可能是这样的:

    contains :: Vertex -> Graph -> Bool
    contains v g@( Graph [] )                 =  False
    contains v g@( Graph ((v0,ngs0):pairs) )  =  (v == v0)  ||  contains v (Graph(pairs))
    

    第二个函数有点复杂,因为列表可以通过 2 种模式来描述,空的和非空的。

    最后,可以像这样编写恰好执行 10 次迭代的函数版本:

    func10 :: Graph -> Graph
    func10 g = funcN 10 g
    

    或者以更简洁的方式使用部分应用程序(在 Haskell 圈子中称为 currying):

    func10 :: Graph -> Graph
    func10 = funcN 10
    

    附录:库样式,使用nest

    如果由于某种原因“手动递归”不受欢迎,可以使用 nest :: Int -&gt; (a -&gt; a) -&gt; a -&gt; a 库函数来代替。它在内部使用递归计算函数的第 N 次组合幂。

    然后只需要编写图函数的单次迭代版本。代码如下所示:

    import Data.Function.HT  (nest)
    
    funcNl :: Int -> Graph -> Graph
    funcNl iterCount g0 = let
                            -- 2 local function definitions:
                            ftfn g1 (v, ngs)        =  contains v g1
                            func1 g2@(Graph(pairs)) =  Graph (filter (ftfn g2) pairs)
                          in
                            nest iterCount func1 g0
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-06
      • 1970-01-01
      • 2020-04-04
      • 1970-01-01
      • 2023-03-27
      • 2018-12-27
      相关资源
      最近更新 更多