【问题标题】:Refactor Haskell monadic code to avoid copy-paste重构 Haskell monadic 代码以避免复制粘贴
【发布时间】:2017-08-17 15:56:51
【问题描述】:

我在 Haskell 中使用 ST monad 编写了以下代码,它可以工作。我唯一的问题是如何避免下面代码中显示的复制粘贴。当我尝试重构代码时,我遇到了无法完全理解的编译器错误。有没有办法避免下面代码中的复制粘贴。我想知道,是否可以将处理 start_1 和 start_2 的代码(现在复制/粘贴)重构为另一个辅助函数。

import qualified Control.Monad as CM
import qualified Control.Monad.ST as CMST
import qualified Data.Array as A
import qualified Data.Array.Unboxed as AU
import qualified Data.Array.ST as AST


prime_factors :: Int -> Int -> [(Int, Int)] -> A.Array Int [(Int, Int)]
prime_factors a_low a_high prms_sqrts = AST.runSTArray $ do
  pfs <- AST.newArray (a_low, a_high) [] :: CMST.ST s (AST.STArray s Int [(Int, Int)])
  as <- AST.newArray (a_low, a_high) 0 :: CMST.ST s (AST.STArray s Int Int)
  CM.forM_ [a_low..a_high] $ \i -> do
    AST.writeArray as i ((i * i) + 1)
  CM.forM_ (takeWhile (\(prm, _) -> prm <= (a_high + 1)) prms_sqrts) $ \(prm, sqr_rt) -> do
    let (q, r) = a_low `divMod` prm
    let start_1 = a_low + sqr_rt - r
    -- !!!! CODE TO DO SOME PROCESSING FOR start_1 !!!!!
    CM.forM_ (takeWhile (<= a_high) [start_1 + (x * prm) | x <- [0..]]) $ \i -> do
      a_i <- AST.readArray as i
      let (a_i', mul) = remove_factor a_i prm 0
      CM.when (mul > 0) $ do
        AST.writeArray as i a_i'
        pfs_i <- AST.readArray pfs i
        AST.writeArray pfs i ((prm, mul) : pfs_i)
    let start_2 = a_low + (prm - sqr_rt) - r
    -- !!!! COPY-PASTE ABOVE CODE TO PROCESS start_2 !!!!
    CM.forM_ (takeWhile (<= a_high) [start_2 + (x * prm) | x <- [0..]]) $ \i -> do
      a_i <- AST.readArray as i
      let (a_i', mul) = remove_factor a_i prm 0
      CM.when (mul > 0) $ do
        AST.writeArray as i a_i'
        pfs_i <- AST.readArray pfs i
        AST.writeArray pfs i ((prm, mul) : pfs_i)
  CM.forM_ [a_low..a_high] $ \i -> do
    a_i <- AST.readArray as i
    CM.when (a_i > 1) $ do
      pfs_i <- AST.readArray pfs i
      AST.writeArray pfs i ((a_i, 1) : pfs_i)
  return pfs
  where remove_factor m p mul
          | m `mod` p == 0 = remove_factor (m `div` p) p (mul + 1)
          | otherwise = (m, mul)

当我尝试将上述复制粘贴代码移动到使用“let”绑定创建的本地函数(名为 sieve_factors)时收到的错误消息是:

vamsi@vamsi-laptop:~/learn/project_euler/129_to_256/problem_224$ ghc --make -O -i../.. problem_224.hs
[3 of 3] Compiling Main             ( problem_224.hs, problem_224.o )

problem_224.hs:39:9: error:
    • Non type-variable argument
        in the constraint: AST.MArray (AST.STArray s) Int m
      (Use FlexibleContexts to permit this)
    • When checking the inferred type
        sieve_factors :: forall (m :: * -> *).
                         (AST.MArray (AST.STArray s) Int m,
                          AST.MArray (AST.STArray s) [(Int, Int)] m) =>
                         Int -> m ()
      In the expression:
        do { let sieve_factors start = ...;
             let (q, r) = a_low `divMod` prm;
             let start_1 = a_low + sqr_rt - r;
             sieve_factors start_1;
             .... }
      In the second argument of ‘($)’, namely
        ‘\ (prm, sqr_rt)
           -> do { let ...;
                   let ...;
                   .... }’

【问题讨论】:

  • 您对哪种重构感兴趣?您从 GHC 收到了哪些错误消息?
  • 请始终在问题中添加相关的导入语句。
  • @RodrigoRibeiro 我想将上面代码中突出显示的处理 start_1 和 start_2 的代码移到另一个函数,我可以简单地调用两次。
  • @leftaroundabout 添加了导入。

标签: haskell st-monad


【解决方案1】:

编译器告诉你

Use FlexibleContexts to permit this

这意味着启用FlexibleContexts language extension。您可以通过在源文件的顶部添加 LANGUAGE pragma 来做到这一点

{-# LANGUAGE FlexibleContexts #-}

【讨论】:

  • 谢谢,补充说解决了这个问题。但是,我宁愿不使用我不完全理解的语言功能,所以我保留复制粘贴,我不明白为什么没有任何扩展的常规 Haskell 允许我将一些复制粘贴代码重构为不同的功能.
  • 此时担心编写 Haskell 98 代码(如 1998 年)几乎毫无意义。 GHC 现在是 Haskell 的事实标准。 FlexibleContextsFlexibleInstancesMultiParamTypeClasses 应该是语言规范的一部分。您已经通过Data.Array 使用MultiParamTypeClasses,因为类型类MArray 有多个参数。您已经在使用FlexibleInstances,因为STArrayMArray 实例需要它。需要FlexibleContexts 是使用FlexibleInstances 的必然结果。它们只是还没有出现在您的代码中。
  • 感谢您的澄清。我对 FlexibleContexts、FlexibleInstances 和 MultiParamTypeClasses 不太熟悉。我会尝试了解他们。另外,我们没有 1998 年之后的 Haskell 标准,即 Haskell 2010 吗?基于此链接,Haskell 2010 您提到的功能也不在 Haskell 2010 中。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-06-07
  • 1970-01-01
  • 2019-09-28
  • 2016-03-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多