2012-05-15 69 views
2

我有一個在父函數中實例化的結構,我想通過調用該父函數的函數來修改該實例化的數據。這裏是一個人爲的例子:完成這項任務有哪些選擇?

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(哈斯克爾超級馬里奧克隆)。你會看到state.monad從不使用,並且有上層結構必須在整個代碼中流動(例如,Main.hs中的GameGame)。

+0

你期望輸出什麼?有int列表進行排序? – epsilonhalbe

+0

輸出與預期的一樣('MyLists {myInts = [1,2,3,4],myBools = [False,True,False]}')。我正在尋找更好的方式來構建這些代碼。 – joshj

+0

在'addBoolToList True my_main_lists'中,不應該是'my_new_main_lists'? –

回答

3

你可以把它一點點更簡潔:

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

addIntToList :: Int -> MyLists -> MyLists 
addIntToList x [email protected]{..} = 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 … 

至於在程序中使用MyLists,該StateT單子轉換是非常有幫助的:

{-# 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

該擴展確實使記錄工作變得更容易,但有沒有更好的方法來構建代碼?而且,我意識到使用Data.List.x是冗長的,但是在閱讀和學習其他人的源代碼時,我發現haskellers應該更加冗長。那個,或者有人需要提出更好的語義突出顯示,或者可靠的工具提示指示函數來自哪裏。 – joshj

+0

@ user175492:*「該擴展確實使記錄更容易處理,但有沒有更好的方法來構造代碼?」*您是在談論使用「MyLists」的代碼還是訪問器本身? –

+0

@ user175492:*「而且,我意識到使用Data.List.x是冗長的,但是......」*夠公平的。我建議'導入限定的Data.List',因爲如果沒有'qualified',它會將'Data.List'中的所有名稱轉儲到命名空間中。 –

1

我不是一個職業,但是這是我怎麼會嘗試使用RecordWildCards擴展來實現這一

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 
+0

不是我在找什麼;我對構造問題感興趣。我編輯了我的原始帖子,使其更清晰。 – joshj

2

你應該看看Data.LensMonadState

{-# 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-lens,data-lens-fddata-lens-template模塊(使用cabal install)。

+0

我想玩這個,但它不編譯。我在編輯中添加了我遇到的問題,但需要同行評審。 – joshj