7

単純な関数構成のための「do表記法」の構文糖衣はありますか?

(つまり(.) :: (b -> c) -> (a -> b) -> a -> c)

いくつかのコンポジションの結果を後で保存できるようにしたいと思います (チェーンを継続しながら.

可能であれば、RebindableSyntax 拡張機能は使用したくありません。

私はこのようなものを探しています:

composed :: [String] -> [String]
composed = do
    fmap (++ "!!!")
    maxLength <- maximum . fmap length
    filter ((== maxLength) . length)

composed ["alice", "bob", "david"]
-- outputs: ["alice!!!", "david!!!"]

以前の関数の結果は本質的に maxLength のバインドを「通過」する必要があるため、このようなことが可能かどうかはわかりませんが、他の同様の表現力のあるオプションについては聞いてみましょう。基本的には、構成を進めながら情報を収集し、後で使用できるようにする必要があります。

おそらく私は状態モナドでこのようなことをすることができますか?

ご協力いただきありがとうございます!

編集

この種のことはちょっとうまくいきます:

split :: (a -> b) -> (b -> a -> c) -> a -> c
split ab bac a = bac (ab a) a

composed :: [String] -> [String]
composed = do
    fmap (++ "!!!")
    split 
        (maximum . fmap length)
        (\maxLength -> (filter ((== maxLength) . length)))
4

4 に答える 4

3

(<*>)の関数インスタンスに特化した型Applicativeは次のとおりです。

(<*>) :: (r -> a -> b) -> (r -> a) -> (r -> b)

結果のr -> b関数は、その引数を関数r -> a -> bと関数の両方に渡し、関数によって生成された値を関数の 2 番目の引数としてr -> a使用します。ar -> ar -> a -> b

これはあなたの機能と何の関係がありますか? filter述語とリストの 2 つの引数の関数です。ここで、実行しようとしていることの重要な側面は、述語がリストから生成されることです。つまり、関数のコアは次のように表現できます(<*>)

-- Using the predicate-generating function from leftaroundabout's answer.
maxLengthOnly :: Foldable t => [t a] -> [t a]
maxLengthOnly = flip filter <*> ((. length) . (==) . maximum . fmap length)

composed :: [String] -> [String]
composed = maxLengthOnly . fmap (++ "!!!")

maxLengthOnlyポイントフリー述語生成関数がそれほどぎこちなくなければ、この定義は非常に優れたワンライナーになります。

関数のApplicativeインスタンスは、関数のインスタンスと能力が同等であるためMonad、次のmaxLengthOnlyように表現することもできます。

maxLengthOnly = (. length) . (==) . maximum . fmap length >>= filter

splitちなみに、質問に追加したのは(>>=)関数用です。)

別の書き方Applicativeは次のとおりです。

maxLengthOnly = filter <$> ((. length) . (==) . maximum . fmap length) <*> id

これが leftaroundabout のソリューションによく似ているのは偶然ではありません(,) <$> f <*> g = liftA2 (,) f g = f &&& g

id最後に、最新バージョンの をmaxLengthOnlywithに置き換えたくfmap (++ "!!!")なるかもしれませんが、これは機能しませんfmap (++ "!!!")。文字列の長さが変わるため、述語の結果に影響することにも注意してください。ただし、述語を無効にしない関数を使用すると、かなりうまく機能します。

nicerComposed = filter
    <$> ((. length) . (==) . maximum . fmap length) <*> fmap reverse
GHCi> nicerComposed ["alice","bob","david"]
["ecila","divad"]
于 2016-11-10T05:21:24.537 に答える
1

あなたが持っているのは本質的にフィルターですが、リストを反復処理するとフィルター機能が変化するものです。これを「分岐した」構成としてではなく、次の関数を使用して折り畳みとしてモデル化しますf :: String -> (Int, [String])

  1. 戻り値は、現在の最大値とその長さのすべての文字列を保持します。
  2. 最初の引数が現在の最大値よりも短い場合は、それを削除します。
  3. 最初の引数が現在の最大値と同じである場合は、それをリストに追加します。
  4. 最初の引数がより長い場合、その長さを新しい最大値にし、現在の出力リストを新しいリストに置き換えます。

折り畳みが完了したら、タプルからリストを抽出するだけです。

-- Not really a suitable name anymore, but...
composed :: [String] -> [String]
composed = snd . foldr f (0, [])
    where f curr (maxLen, result) = let currLen = length curr
                                    in case compare currLen maxLen of
                                       LT -> (maxLen, result)       -- drop
                                       EQ -> (maxLen, curr:result)  -- keep
                                       GT -> (length curr, [curr])  -- reset
于 2016-11-10T04:50:17.377 に答える