32

Haskell プログラミング言語のあまり知られていないが便利な機能は何ですか。(言語自体はあまり知られていないことは理解していますが、私と一緒に作業します。1 行のコードでフィボナッチ数列を定義するなど、Haskell の単純なことの説明でさえ、私は支持します。)

  • Haskellコアへの回答を制限してみてください
  • 回答ごとに 1 つの機能
  • ドキュメントへのリンクだけでなく、機能の例と簡単な説明を提供してください
  • 最初の行として太字のタイトルを使用してフィーチャにラベルを付けます
4

25 に答える 25

40

ユーザー定義の制御構造

Haskell には省略形の三項演算子はありません。組み込みのif- then-は常に 3 項式であり、式です (命令型言語には=expression、=statementが含まelseれる傾向があります)。ただし、必要に応じて、?:if

True ? x = const x
False ? _ = id

(?)三項演算子であると定義します。

(a ? b $ c)  ==  (if a then b else c)

他のほとんどの言語では、独自の短絡論理演算子を定義するためにマクロを使用する必要がありますが、Haskell は完全に怠惰な言語であるため、問題なく機能します。

-- prints "I'm alive! :)"
main = True ? putStrLn "I'm alive! :)" $ error "I'm dead :("
于 2008-10-17T21:49:02.253 に答える
25

私の脳はちょうど爆発した

このコードをコンパイルしようとすると:

{-# LANGUAGE ExistentialQuantification #-}
data Foo = forall a. Foo a
ignorefoo f = 1 where Foo a = f

次のエラー メッセージが表示されます。

$ ghc Foo.hs

フー.hs:3:22:
    私の脳はちょうど爆発しました。
    存在量化されたコンストラクターのパターン バインディングを処理できません。
    代わりに、case 式または do 表記を使用して、コンストラクターをアンパックします。
    のバインディンググループで
        フーア
    パターンバインディング: Foo a = f
    `ignorefoo' の定義では:
        無視foo f = 1
                    どこ
                        フー a = f
于 2008-10-17T19:02:06.490 に答える
22

自由定理

Phil Wadler は自由定理の概念を私たちに紹介し、それ以来、私たちは Haskell でそれらを悪用してきました。

Hindley-Milner スタイルの型システムのこれらのすばらしいアーティファクトは、パラメトリック性を使用して関数が実行しないことを伝えることにより、等式の推論に役立ちます。

たとえば、Functor のすべてのインスタンスが満たすべき 2 つの法則があります。

  1. forall f g. fmap f . fmap g = fmap (f . g)
  2. fmap id = id

しかし、自由定理は、最初の定理をわざわざ証明する必要はないことを教えてくれますが、2 番目の定理を考えると、型シグネチャからだけで「自由」になります!

fmap :: Functor f => (a -> b) -> f a -> f b

怠惰には少し注意する必要がありますが、これは元の論文との存在下での自由定理に関するJanis Voigtlaender の最近の論文seqで部分的にカバーされています。

于 2009-07-06T17:46:13.053 に答える
20

共通リスト操作の省略形

以下は同等です。

concat $ map f list
concatMap f list
list >>= f

編集

詳細を求められたので...

concat :: [[a]] -> [a]

concatリストのリストを受け取り、それらを単一のリストに連結します。

map :: (a -> b) -> [a] -> [b]

map関数をリストにマップします。

concatMap :: (a -> [b]) -> [a] -> [b]

concatMapと同等です(.) concat . map: リストに関数をマップし、結果を連結します。

class Monad m where
    (>>=) :: m a -> (a -> m b) -> m b
    return :: a -> m a

AMonadにはバインド操作があり>>=、Haskell (またはそれにdo相当するもの) で呼び出されます。リスト、別名[]は ですMonad。上記を代入[]するmと:

instance Monad [] where
    (>>=) :: [a] -> (a -> [b]) -> [b]
    return :: a -> [a]

Monadリストに対して操作を行うのに自然なことは何ですか? モナド則を満たさなければなりません。

return a >>= f           ==  f a
ma >>= (\a -> return a)  ==  ma
(ma >>= f) >>= g         ==  ma >>= (\a -> f a >>= g)

実装を使用すると、これらの法則が成立することを確認できます

instance Monad [] where
    (>>=) = concatMap
    return = (:[])

return a >>= f  ==  [a] >>= f  ==  concatMap f [a]  ==  f a
ma >>= (\a -> return a)  ==  concatMap (\a -> [a]) ma  ==  ma
(ma >>= f) >>= g  ==  concatMap g (concatMap f ma)  ==  concatMap (concatMap g . f) ma  ==  ma >>= (\a -> f a >>= g)

実際、これは の動作ですMonad []。デモンストレーションとして、

double x = [x,x]
main = do
    print $ map double [1,2,3]
        -- [[1,1],[2,2],[3,3]]
    print . concat $ map double [1,2,3]
        -- [1,1,2,2,3,3]
    print $ concatMap double [1,2,3]
        -- [1,1,2,2,3,3]
    print $ [1,2,3] >>= double
        -- [1,1,2,2,3,3]
于 2008-10-17T18:14:31.157 に答える
19

一般化された代数的データ型。型システムですべてのケースをカバーできるインタプリタの例を次に示します。

{-# LANGUAGE GADTs #-}
module Exp
where

data Exp a where
  Num  :: (Num a) => a -> Exp a
  Bool :: Bool -> Exp Bool
  Plus :: (Num a) => Exp a -> Exp a -> Exp a
  If   :: Exp Bool -> Exp a -> Exp a -> Exp a 
  Lt   :: (Num a, Ord a) => Exp a -> Exp a -> Exp Bool
  Lam  :: (a -> Exp b) -> Exp (a -> b)   -- higher order abstract syntax
  App  :: Exp (a -> b) -> Exp a -> Exp b
 -- deriving (Show) -- failse

eval :: Exp a -> a
eval (Num n)      = n
eval (Bool b)     = b
eval (Plus e1 e2) = eval e1 + eval e2
eval (If p t f)   = eval $ if eval p then t else f
eval (Lt e1 e2)   = eval e1 < eval e2
eval (Lam body)   = \x -> eval $ body x
eval (App f a)    = eval f $ eval a

instance Eq a => Eq (Exp a) where
  e1 == e2 = eval e1 == eval e2

instance Show (Exp a) where
  show e = "<exp>" -- very weak show instance

instance (Num a) => Num (Exp a) where
  fromInteger = Num
  (+) = Plus
于 2009-04-15T01:54:02.467 に答える
19

ネスト可能な複数行のコメント

{- inside a comment,
     {- inside another comment, -}
still commented! -}
于 2008-10-17T19:00:43.560 に答える
18

トップレベル バインディングのパターン

five :: Int
Just five = Just 5

a, b, c :: Char
[a,b,c] = "abc"

なんてクールだ!その電話を時々保存しfromJustますhead

于 2009-06-05T08:54:07.330 に答える
17

オプションのレイアウト

空白 (別名レイアウト) の代わりに明示的な中括弧とセミコロンを使用して、ブロックを区切ることができます。

let {
      x = 40;
      y = 2
     } in
 x + y

...または同等に...

let { x = 40; y = 2 } in x + y

... それ以外の ...

let x = 40
    y = 2
 in x + y

レイアウトが不要なため、Haskell プログラムは他のプログラムで簡単に作成できます。

于 2008-10-17T13:00:04.660 に答える
16

オペレーターの固執

infix、infixl、または infixrキーワードを使用して、演算子の結合性と優先順位を定義できます。リファレンスからの例:

main = print (1 +++ 2 *** 3)

infixr  6 +++
infixr  7 ***,///

(+++) :: Int -> Int -> Int
a +++ b = a + 2*b

(***) :: Int -> Int -> Int
a *** b = a - 4*b

(///) :: Int -> Int -> Int
a /// b = 2*a - 3*b
Output: -19

中置記号の後の数字 (0 から 9) を使用すると、演算子の優先順位を定義できます。9 が最も強力です。Infix は結合性がないことを意味しますが、infixl は​​左に結合し、infixr は右に結合します。

これにより、複雑な演算子を定義して、単純な式として記述された高レベルの操作を実行できます。

バッククォートの間に配置すると、二項関数を演算子として使用することもできることに注意してください。

main = print (a `foo` b)

foo :: Int -> Int -> Int
foo a b = a + b

そのため、それらの優先順位を定義することもできます:

infixr 4 `foo`
于 2008-10-17T13:27:09.333 に答える
16

seq何かが底ではないことを確認するのに十分なだけ($!) 評価します。

次のプログラムは、「そこ」のみを出力します。

main = print "hi " `seq` print "there"

Haskell に慣れていない方のために説明すると、Haskell は一般的に厳密ではありません。つまり、関数の引数は必要な場合にのみ評価されます。

たとえば、次の例では「無視されました」と出力され、正常に終了します。

main = foo (error "explode!")
  where foo _ = print "ignored"

seq最初の引数が下の場合、下に評価することによってその動作を変更することが知られています。

例えば:

main = error "first" `seq` print "impossible to print"

... または同等に、中置記号なし ...

main = seq (error "first") (print "impossible to print")

...「最初」のエラーで爆発します。「印刷不可」と表示されることはありません。

したがって、厳密であるにもかかわらず、seq積極的な言語が評価する方法で何かを評価しないことは少し驚くかもしれません。特に、次のプログラムではすべての正の整数を強制しようとはしません。代わりに、それが[1..]底ではないことを確認し (これはすぐに見つかります)、「完了」と出力して終了します。

main = [1..] `seq` print "done"
于 2008-10-17T13:34:56.540 に答える
15

C スタイルの列挙

トップレベルのパターン マッチングと算術シーケンスを組み合わせることで、連続する値を簡単に定義できます。

foo : bar : baz : _ = [100 ..]    -- foo = 100, bar = 101, baz = 102
于 2009-12-14T14:12:35.123 に答える
15

括弧を避ける

および関数には非常に便利な固定機能が(.)あり、多くの場所で括弧を避けることができます。以下は同等です。($)Prelude

f (g (h x))
f $ g $ h x
f . g $ h x
f . g . h $ x

flipも役立ちます。以下は同等です。

map (\a -> {- some long expression -}) list
flip map list $ \a ->
    {- some long expression -}
于 2008-10-17T19:00:19.880 に答える
15

かわいい警備員

Preludeを定義しotherwise = True、完全なガード条件を非常に自然に読み取らせます。

fac n
  | n < 1     = 1
  | otherwise = n * fac (n-1)
于 2008-10-17T19:01:06.087 に答える
13

読み取り可能な関数構成

Prelude(.)数学関数の合成であると定義します。つまり、g . f最初に適用されf、次にg結果に適用されます。

の場合import Control.Arrow、以下は同等です。

g . f
f >>> g

Control.Arrowを提供します。instance Arrow (->)これは、関数適用を逆方向に読みたくない人に便利です。

于 2008-10-17T18:59:43.157 に答える
11

let 5 = 6 in ...有効な Haskell です。

于 2009-10-08T08:51:26.090 に答える
10

無限リスト

フィボナッチについて言及したので、次のような無限リストからフィボナッチ数を生成する非常にエレガントな方法があります。

fib@(1:tfib)    = 1 : 1 : [ a+b | (a,b) <- zip fib tfib ]

@ 演算子を使用すると、パターン全体を fib として参照しながら、1:tfib 構造でパターン マッチングを使用できます。

内包リストは無限再帰に入り、無限リストを生成することに注意してください。ただし、有限の量を要求する限り、そこから要素を要求したり、それらを操作したりできます。

take 10 fib

リクエストする前に、すべての要素に操作を適用することもできます。

take 10 (map (\x -> x+1) fib)

これは Haskell のパラメーターとリストの遅延評価のおかげです。

于 2008-10-17T16:02:12.780 に答える
9

モジュールのインポートとエクスポートの柔軟な仕様

インポートとエクスポートは素晴らしいです。

module Foo (module Bar, blah)  -- this is module Foo, export everything that Bar expored, plus blah

import qualified Some.Long.Name as Short
import Some.Long.Name (name)  -- can import multiple times, with different options

import Baz hiding (blah)  -- import everything from Baz, except something named 'blah'
于 2008-10-17T19:01:51.123 に答える
8

等式推論

Haskellは、純粋関数型であるため、等号を実際の等号として読み取ることができます(重複しないパターンがない場合)。

これにより、定義をコードに直接置き換えることができ、最適化の観点から、何かがいつ発生するかについてコンパイラーに多くの余裕が与えられます。

この形式の推論の良い例はここにあります:

http://www.haskell.org/pipermail/haskell-cafe/2009-March/058603.html

これは、インスタンスの有効なメンバーに期待される法則またはルールプラグマの形でもうまく現れます。たとえば、モナド法則です。

  1. a >> = f==faを返します
  2. m>>=リターン==m
  3. (m >> = f)>> = g == m >> =(\ x-> fx >> = g)

多くの場合、モナディックコードを単純化するために使用できます。

于 2009-07-06T17:38:53.503 に答える
8

リストや高階関数を探しているなら、それはすでにそこにあります

標準ライブラリには、非常に多くの便利な高階関数があります。

-- factorial can be written, using the strict HOF foldl':
fac n = Data.List.foldl' (*) 1 [1..n]
-- there's a shortcut for that:
fac n = product [1..n]
-- and it can even be written pointfree:
fac = product . enumFromTo 1
于 2008-10-17T19:01:30.920 に答える
6

怠惰

ユビキタスな怠惰とは、定義するようなことができることを意味します

fibs = 1 : 1 : zipWith (+) fibs (tail fibs)

しかし、構文と推論の点で、より多くの微妙な利点も提供します。

たとえば、厳密さのために、MLは値の制限に対処する必要があり、循環letバインディングを追跡するのに非常に注意が必要ですが、Haskellでは、すべてのletを再帰的にすることができ、とを区別する必要はありませvalfun。これにより、言語から主要な構文上の疣贅が削除されます。

これは間接的に私たちの素敵なwhere節を生み出します。なぜなら、使用されるかもしれないし使用されないかもしれない計算をメインの制御フローから安全に移動し、怠惰に結果の共有を処理させることができるからです。

()を取り、値を返す必要のあるMLスタイルの関数の(ほぼ)すべてを、値の怠惰な計算だけで置き換えることができます。CAFでスペースが漏れないようにするために、時々そうすることを避ける理由がありますが、そのような場合はまれです。

最後に、無制限のeta-reductionを許可\x -> f xします(fに置き換えることができます)。これにより、パーサーコンビネーターなどのコンビネーター指向プログラミングは、厳密な言語で同様の構造を操作するよりもはるかに快適になります。

これは、ポイントフリースタイルのプログラムについて推論するとき、またはプログラムをポイントフリースタイルに書き直すときに役立ち、引数のノイズを減らします。

于 2009-07-07T13:00:51.723 に答える
6

強化されたパターン マッチング

  • 怠惰なパターン
  • 反駁できないパターン

    let ~(Just x) = someExpression
    

パターンマッチングを見る

于 2009-07-06T18:26:23.750 に答える
6

並列リスト内包表記

(GHC特集)

  fibs = 0 : 1 : [ a + b | a <- fibs | b <- tail fibs ]
于 2009-07-06T18:31:18.843 に答える
5

列挙

Enumのインスタンスである型は、数値だけでなく、算術シーケンスで使用できます。

alphabet :: String
alphabet = ['A' .. 'Z']

独自のデータ型を含めて、Enum から派生させてデフォルトの実装を取得します。

data MyEnum = A | B | C deriving(Eq, Show, Enum)

main = do
    print $ [A ..]                 -- prints "[A,B,C]"
    print $ map fromEnum [A ..]    -- prints "[0,1,2]"
于 2009-12-14T14:06:08.367 に答える
3

モナド

それらはそれほど隠されているわけではありませんが、あなたがそれらを考えていない場所(リスト、Maybe-Types)であっても、どこにでもあります...

于 2009-07-06T18:28:24.807 に答える