完成同样任务有哪些替代方案?

时间:2012-05-15 17:52:41

标签: haskell

我有一个在父函数中实例化的结构,我想通过调用该父函数中的函数来修改该实例化数据。这是一个人为的例子:

import Data.List

data MyLists = MyLists {
    myInts :: [Int],
    myBools :: [Bool]
} deriving (Show)

addIntToList :: Int -> MyLists -> MyLists
addIntToList x main_lists =
    main_lists { myInts = Data.List.insert x my_ints }
    -- might make a call to another child function that modifies main_list here, and so on (i.e., this is the vertical problem I see with this structuring)
        where
            my_ints = myInts main_lists

main :: IO()
main = do
    let my_main_lists = MyLists [1,2,3] [False, True, False]
    let my_new_main_lists = addIntToList 4 my_main_lists
    print my_new_main_lists
    let my_new_new_main_lists = addBoolToList True my_new_main_lists
    print my_new_new_main_lists
    -- and so on (this is the lateral problem I see with this code structuring)

构建此代码或完成与此类似的任务有哪些替代方法?有更简洁的方法吗?

我应该补充说,一旦你对子函数进行了长链函数调用,这会变得特别臭(即代码气味);他们最终都需要返回一个新的MyLists或只是返回main_list而没有做任何事情。那,父母可能还必须处理MyList和另一个返回值(例如-> (Bool, MyList))。

因此,您可以想象函数调用的树结构都需要MyList参数并返回值;这似乎不是最佳的。

这是我正在谈论的那种事情的更具体的例子。浏览https://github.com/mokehehe/monao处的代码(haskell中的超级马里奥克隆)。您将看到state.monad从未被使用,并且有必须在整个代码中流动的上层结构(例如,Main.h中的GameGame)。

3 个答案:

答案 0 :(得分:3)

您可以使用RecordWildCards扩展程序使其更简洁:

{-# LANGUAGE RecordWildCards #-}
import Data.List (insert)

addIntToList :: Int -> MyLists -> MyLists
addIntToList x ml@MyLists{..} = ml{ myInts = insert x myInts }

MyLists{..}模式将MyLists记录的属性转储到范围中。因此,我们可以在初始化新myInts时轻松引用旧版myInts

相反,如果在表达式上下文中使用..,它将使用范围中的相应名称填充未初始化的属性。 addIntToList函数也可以写成:

addIntToList x MyLists{..} = MyLists{ myInts = insert x myInts, .. }

使用记录通配符可以做的一件很酷的事情:

someAction = do
    myInts  <- getInts :: IO [Int]
    myBools <- getBools :: IO [Bool]
    return MyLists{..}

此外,Data.List.insert有点冗长。你可以说insert,因为这个导入:

import Data.List

会将Data.List中的所有名称导入名称空间。如果你不喜欢这个(例如因为你想在你自己的模块中定义一个名为insert的函数),你可以导入它合格:

import qualified Data.List as List

… List.insert …

至于在程序中使用MyListsStateT monad变换器非常有帮助:

{-# LANGUAGE RecordWildCards #-}
import Control.Monad.State

...

printList :: StateT MyLists IO ()
printList = liftIO . print =<< get

program :: StateT MyLists IO ()
program = do
    printList
    modify $ addIntToList 4
    printList
    modify $ addBoolToList True
    printList

main :: IO()
main = evalStateT program $ MyLists [1,2,3] [False, True, False]

答案 1 :(得分:2)

您应该使用Data.Lens

查看MonadState
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE FlexibleContexts #-}

import Control.Monad.State
import Data.Lens
import Data.Lens.Template
import qualified Data.List

data MyLists = MyLists { _myInts :: [Int]
                       , _myBools :: [Bool]
                       } deriving ( Show )

$(makeLens ''MyLists)

addIntToList :: MonadState MyLists m => Int -> m [Int]
addIntToList i = myInts %= Data.List.insert i

addBoolToList :: MonadState MyLists m => Bool -> m [Bool]
addBoolToList b = myBools %= Data.List.insert b

program :: MonadState MyLists m => m ()
program = do
    addIntToList 1
    addBoolToList False
    addIntToList 2
    addBoolToList True
    return ()

main = do
  let ml = execState program MyLists { _myInts = []
                                     , _myBools = []
                                     }
  print ml

修改

这就是我输入未经测试的代码所得到的。我改变了这个例子,所以它确实有效!您需要安装data-lensdata-lens-fddata-lens-template模块(使用cabal install)。

答案 2 :(得分:1)

我不是专业人士,但我会尝试实现这个目标

import Data.List (insert)

data MyLists = MyLists { myInts  :: [Int],
                         myBools :: [Bool]
                       } deriving (Show)

addIntToList :: Int -> MyLists -> MyLists
addIntToList i (MyLists is bs) = MyLists (insert i is) bs
-- if you don't care about order just do (i:is), which is faster
-- usually lists of i are denoted is (or sometimes I use ii) as a kind of plural

main :: IO()
main = do
    let myMainList = MyLists [1,2,3] [False, True, False]
    -- you would rather use CamelCase in Haskell than _
    print $ addIntToList 4 myMainList