3

重複の可能性:
なぜそのような関数定義が haskell で許可されないのですか?

という Haskell 関数を作りましたfunlist。それが行うことは、開始値と関数のリストを取り、リスト内のすべての関数を開始値に適用することです。

funlist thing [function] = function thing
funlist thing (function:functions) = funlist (function thing) functions
funlist _ _ = error "need a list of functions"

この関数の問題は、型がfunlist :: t -> [t -> t] -> t. その型は、ghc が開始値を完全に異なる型に変換しない関数のリストを許可する一方で (たとえば[sin,cos,tan]許可される)、開始値を別の型に変換する関数 (たとえばshow) がエラーを生成することを意味します。その関数は型シグネチャと一致しないためです。

これは、関数が機能する方法ではありません。開始値の型を変更する関数のリストを取得できる必要があります (例: [sin,show])。この関数は基本的に に変換funlist 5 [sin,cos,tan,isInfinite,show]されshow $ isInfinite $ tan $ cos $ sin $ 5、後者は機能しますが、前者は機能しません。

この機能を正しく動作させる方法はありますか?

編集:私は知っています.>>>、これを機能させる方法があるかどうか疑問に思っています。

4

7 に答える 7

7

GADT を使用して、必要なものを記述できます。

{-# LANGUAGE GADTs #-}
module Funlist where

data F x y where
  Id :: F a a
  Ap :: (a->b) -> F b c -> F a c

-- A very round about way to write f x = x + x

f1 :: Int -> Char
f1 = toEnum

f2 :: Char -> String
f2 x = x:x:[]

f3 :: String -> [Int]
f3 = map fromEnum

f4 :: [Int] -> Integer
f4 = foldr (+) 0 . map toInteger

f_list :: F Int Integer
f_list = Ap f1 (Ap f2 (Ap f3 (Ap f4 Id)))

ap :: F a b -> a -> b
ap Id x = x
ap (Ap f gs) x = ap gs (f x)

ap f_list 65130

于 2012-07-18T19:37:51.110 に答える
6

Haskell のような静的に型付けされた言語ではなく、動的に型付けされた言語が必要なため、これは Haskell の通常の関数/通常のリストでは機能しません。関数は、実行時のfunlist関数リストの内容に応じて異なる型を持つことはできません。その型はコンパイル時にわかっている必要があります。[tan, show, sin]さらに、たとえばリストを使用できないように、コンパイラは関数チェーンが有効であることを確認できる必要があります。

この問題には 2 つの解決策があります。

異種リストを使用することもできます。これらのリストには、各要素が異なるタイプのリストを格納できます。次に、各要素が関数でなければならず、1 つの要素の戻り値の型が次の関数のパラメーター型でなければならないという制約を確認できます。これはすぐに非常に困難になる可能性があります。

を使用Data.Dynamicして、関数に動的な型を取り、返すようにすることもできます。その場合、いくつかの動的型キャストを実行する必要があります。

于 2012-07-18T19:25:30.623 に答える
6

この関数のリストを使用してパイプライン内の単一の値に適用するだけの場合は、funlist関数を記述して呼び出す代わりに、次のようにします。

show . isInfinite . tan . cos . sin $ 5

または、コードでリストを逆にしたくない場合は、次のようにします。

import Control.Arrow (>>>)

(sin >>> cos >>> tan >>> isInfinite >>> show) 5
于 2012-07-18T19:29:48.237 に答える
4

a -> b一般に、Haskell の関数は、 と のいずれかを選択して、 のような型を持ちaますb。あなたの場合、[f0, ..., fn]関数のリストがあり、これを計算したい:

funlist [f0, ..., fn] x == f0 (funlist [f1, ..., fn] x)
                        == f0 (f1 (funlist [f2, ..., fn] x))
                        ...
                        == f0 (f1 (... (fn x)))

あなたt -> tが抱えている問題は、次の2つの結果です。

  1. この計算では、 の引数の型f0が の戻り値の型である必要があり、 のf1引数の型が のf1戻り値の型である必要がありf2ますf0 :: y -> z, f1 :: x -> y, ..., fn :: a -> b
  2. しかし、これらすべての関数をリストに入れており、Haskell のリストのすべての要素は同じ型でなければなりません。

これら 2 つを組み合わせると、で使用される関数のリストにfunlistは type が必要であることを意味し[t -> t]ます。これは、両方の条件を同時に満たすことができる唯一の方法だからです。

それ以外は、dave4420の答えが最も簡単な答えです。IMO:関数合成を使用してください。実行する計算が実行時にしかわからないために使用できない場合は、リストよりも複雑なデータ構造を使用して、可能な計算を表す必要があります。Chris Kuklewicz はそのための非常に一般的な解決策を提示していますが、私は通常、目前の特定の問題領域に対してカスタムメイドの何かを行います。

また、次のfunlistように記述できることも知っておくとよいでしょう。

funlist :: a -> [a -> a] -> a
funlist x fs = foldr (.) id fs x
于 2012-07-18T23:44:51.083 に答える
2

短い答え: いいえ、リストでやりたいことを行う方法はありません (少なくとも賢明な方法では)。

その理由は、Haskell のリストは常に同種であるためです。つまり、リストの各要素は同じ型でなければなりません。リストに入れたい関数には次のタイプがあります。

sin :: Floating a => a -> a
isInfinite :: Floating b => b -> Bool
show :: Show c => c -> String

したがって、関数を同じリストに入れることはできません。主なオプションは次の 2 つです。

  1. リスト以外の構造を使用する (例: HList またはカスタム GADT)
  2. 動的型付けを使用する

他の回答では既に GADT の例が示されているため、動的型を使用して関数を実装する方法は次のとおりです。

import Data.Dynamic

funlist :: Dynamic -> [Dynamic] -> Dynamic
funlist thing (function:functions) = funlist (dynApp function thing) functions
funlist thing [] = thing

ただし、動的型を使用すると、静的型と動的型の間で変換する必要があるため、ボイラープレートが発生します。したがって、関数を呼び出すには、次のように記述する必要があります

funlist (toDyn 5) [toDyn sin, toDyn cos, toDyn tan, toDyn isInfinite, toDyn show]

残念ながら、それでも十分ではありません。次の問題は、動的な値には準同型の型が必要なことです。たとえば、関数の代わりにshow :: Show a => a -> String具体的な型などを手動で指定する必要があるshow :: Bool -> Stringため、上記は次のようになります。

funlist (toDyn (5::Double)) [toDyn sin, toDyn cos, toDyn tan, toDyn isInfinite,
    toDyn (show :: Bool -> String)]

さらに、関数の結果は別の動的な値であるため、通常の関数で使用する場合は静的な値に戻す必要があります。

fromDyn (funlist (toDyn (5::Double)) [toDyn sin, toDyn cos, toDyn tan,
    toDyn isInfinite, toDyn (show :: Bool -> String)]) ""
于 2012-07-19T06:33:52.487 に答える
1

あなたが望むものは Haskell で動作しますが、それはリストではありません。これは関数合成であり、実際には GADT でラップできます。

import Control.Arrow
import Control.Category
import Prelude hiding ((.), id)

data Chain :: * -> * -> * where
    Chain :: (a -> c) -> Chain c b -> Chain a b
    Id    :: Chain a a

apply :: Chain a b -> a -> b
apply (Chain f k) x = apply k (f x)
apply Id x          = x

これで、関数チェーンの構造をある程度調べることができます。調べられることはあまりありませんが、さらにChain必要な場合は、メタ情報をコンストラクターに追加できます。

この型は、追加情報を保持する興味深いカテゴリも形成します。

instance Category Chain where
    id = Id

    Id . c           = c
    c  . Id          = c
    c2 . Chain f1 k1 = Chain f1 (c2 . k1)

instance Arrow Chain where
    arr f = Chain f Id

    first (Chain f c) = Chain (first f) (first c)
    first Id = Id
于 2012-07-18T23:39:50.143 に答える
0

GADT を使用した回答がいくつかありますが、これはそのようなことを行うのに適した方法です。ここで追加したいのは、これらの回答で使用されている構造が、より一般的な方法で既に存在するということです。これは、thrist (「type threaded list」)と呼ばれます。

Prelude Data.Thrist> let fs = Cons (show :: Char -> String) (Cons length Nil)
Prelude Data.Thrist> let f = foldl1Thrist (flip (.))  fs
Prelude Data.Thrist> :t fs
fs :: Thrist (->) Char Int
Prelude Data.Thrist> :t f
f :: Char -> Int
Prelude Data.Thrist> f 'a'
3

もちろん、foldl1Thrist (>>>) fs代わりに使用することもできます。スリストは、カテゴリ、アロー、およびモノイド ( を使用appendThrist) を形成することに注意してください。

于 2012-07-19T09:59:18.117 に答える