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(haskellのスーパーマリオクローン)でコードを参照します。state.monadが使用されることはなく、コード全体に流れる必要のある上位レベルの構造体があることがわかります(たとえば、Main.hsのGameGame)。

4

3 に答える 3

3

RecordWildCards拡張機能を使用すると、もう少し簡潔にすることができます。

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

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

このパターンは、レコードのプロパティをスコープMyLists{..}にダンプします。したがって、 new を初期化するときMyListsに old を簡単に参照できます。myIntsmyInts

逆に、..式のコンテキストで使用すると、初期化されていないプロパティがスコープ内の対応する名前で埋められます。addIntToList関数は次のようにも記述できます。

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

レコード ワイルドカードでできるもう 1 つの優れた機能:

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]
于 2012-05-15T18:20:05.560 に答える
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-fd、およびdata-lens-templateモジュールをインストールする必要があります ( を使用cabal install)。

于 2012-05-15T19:19:04.530 に答える
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
于 2012-05-15T18:15:06.450 に答える