4

最近Haskellを勉強し始めました。配列のランダムな要素を選択するプログラムを作成しようとしています:

import System.Random

randomInt :: (Int, Int) -> IO Int
randomInt range = randomRIO range :: IO Int

choseRandom :: [a] -> a
choseRandom list = 
    length list
    >>=
        (\l -> randomInt(0,l-1))
        >>=
            (\num -> (list !! num))

main :: IO ()
main = undefined

次のエラーが表示されます。

Build FAILED

C:\Users\User\Haskell\Real\src\Main.hs: line 7, column 9:
  Couldn't match expected type `IO Int' with actual type `Int'

    In the return type of a call of `length'

    In the first argument of `(>>=)', namely `length list'

    In the first argument of `(>>=)', namely

      `length list >>= (\ l -> randomInt (0, l - 1))'

私が間違っていることは何ですか?初めてモナドを扱うのは難しい

4

5 に答える 5

7

内部で IO を使用しているchoseRandomため、型シグネチャを変更する必要があります。

choseRandom :: [a] -> IO a

>>=次に、リストの長さを取得するためにを使用する必要はありません。>>=タイプあり

Monad m => m a -> (a -> m b) -> m b

の型はlengthis[a] -> Intですので、の型はlength listisIntであり、モナドではありません。

を呼び出すときに直接計算できますrandomInt

choseRandom :: [a] -> IO a
choseRandom list = 
    randomInt(0, length list) >>= (\num -> return (list !! num))

これはと同じです

choseRandom :: [a] -> IO a
choseRandom list = fmap (\num -> (list !! num)) (randomInt(0, length list))
于 2014-04-18T09:32:26.310 に答える
7

初めてモナドを扱うのは難しい

はい、構文サポートを避けることで、それを難し​​くしています。次のように書くだけです:

choseRandom list = do
   let l = length list
   num <- randomInt(0,l-1)
   return (list !! num)

これははるかに良く見えませんか?

要点:randomRIO関数は、型が示すように、何らかのグローバル状態 (おそらくシステム タイマー) を使用します。したがって、モナドRandomRIOでのみ結果を使用できます。IO

別の方法として、関数内で乱数発生器を初期化し、mainこの発生器を「乱数」値を必要とする純粋な関数に渡します。

于 2014-04-18T09:35:02.190 に答える
4

これはうまくいくはずです:

import System.Random

randomInt :: (Int, Int) -> IO Int
randomInt range = randomRIO range :: IO Int

choseRandom :: [b] -> IO b
choseRandom list = randomInt (0, length list) >>= \x -> return $ list !! x

私はこれがより慣用的だと思います:

choseRandom list = do
 a <- randomInt (0, length list)
 return $ list !! a

chooseRandom 関数の問題は、型シグネチャが間違っていることです。の型は である>>=べきなのでm a -> (a -> m b) -> m b、 を使用して結果をモナドにラップする必要がありますreturn

したがって、次のように動作します。

randomInt (0, length list)あなたに与え、そこから抽出する関数をIO Int使用します。これで、関数を使用してリストから対応する要素を抽出できます。しかし、出力型は であるべきなので、を使用してモナドにラップする必要があります。>>=Int!!m breturn

于 2014-04-18T09:34:08.297 に答える
3

まず、型シグネチャ fo を修正する必要がありますchoseRandomdo次に、表記法を使用すると、これがはるかに簡単になると思います。

choseRandom :: [a] -> IO a
choseRandom list = do
  let l = length list
  num <- randomInt (0, l-1)
  return (list !! num)
于 2014-04-18T09:35:22.430 に答える