2017-10-15 23 views
0

私はこの演習を行うことに疑念があります。平均値と最高値を計算するHaskell IOプログラム

デフォルトの入力デバイスから1行に1つずつ、負のユニバースまたはゼロのユニバースに整数を読み込み、平均値と最大値を出力するプログラムを作成します(これは明らかに間違っています)読み取った値の

マイコード:

a6 ::Int -> Float ->Int->Int-> IO() 
a6 cBigger average2 sum2 cCount = do 
c <- getLine 
let digit = read c :: Int 
let sum = sum2 + digit 
let average = fromIntegral sum2/ fromIntegral cCount 
if (digit <=0) 
    then putStrLn("Bigger :" ++show(cBigger)++ "average "++show(cAverage)) 
    else 
    if digit > cBigger 
      then a6 digit average sum (cCount+1) 
      else a6 cBigger average sum (cCount+1) 

私は多くのHaskellのを理解していないとして、私が行う方法の疑問を持っています。

答えて

2

少し話題はありますが、私は懸念とモジュール性の分離についてコメントしたいと思いました。

通常、プログラムの純粋な部分を純粋でない部分(IO)と分けて保存しようとします。

純粋でないコードを持つIntのリストを読んでから、純粋な関数で処理して平均を計算するために最大、合計、および長さを見つけることができます。以下

それは(IOに)リストに正Int Sを返し、非正の値を読み取るまで、readIntsはSTDINからInt Sを読み出します。 maxSumLengthは、これまでに処理された要素の現在の最大値、合計値、および長さをタプルとして扱い、次の要素を折りたたんで新しいタプルを返します。最後にmainIntのリストを読み取り、 maxSumLengthを使用して厳密な左フォールド(foldl')を適用し、最終状態の最大、合計、および長さを計算するために(0, 0, 0)の初期状態を適用します。次に、合計と長さから最大値と平均値を出力します。

module Main where 

import Data.List (foldl') 

readInts :: IO [Int] 
readInts = do 
    i <- read <$> getLine 
    if i <= 0 
    then return [] 
    else (i:) <$> readInts 

maxSumLength :: (Int, Int, Int) -> Int -> (Int, Int, Int) 
maxSumLength (m, s, l) x = (max m x, s+x, l+1) 

main :: IO() 
main = do 
    (m, s, l) <- foldl' maxSumLength (0, 0, 0) <$> readInts 
    putStrLn $ "max=" ++ show m ++ ", avg=" ++ show (fromIntegral s/fromIntegral l) 

このコードは以前よりもモジュール化されています。 Intのリストを必要とする他のプログラムでは、readIntsを再利用することができます。また、アルゴリズムの純粋な部分はもはやIntのリストがどこから来るかを気にしません。しかし、このコードには問題があります。このように記述すると、処理コードが到着したときに入力を消費することができても、純粋なコードが処理を開始する前に、リスト全体をメモリにバッファリングする必要があります。

これは、conduitパッケージが役立つところです。 conduitパッケージを使用すると、不純なストリームを生成し、Consumerに接続して、純粋なコードを不純なコードとインターリーブすることができます。 conduit-combinatorsパッケージでは、ストリームをリストのように扱うことができるコンビネータが用意されています(特に、foldlCはリストの代わりにコンジットストリームで厳密な左折を実行できます)。以下のコードで

readInts機能についてIOモナドで実行SourceIntのsです。 repeatWhileMCコンビネータを使用して、ループ処理と終了テストを実行します。純粋なmaxSumLengthは変更されません。しかし、mainでは、foldl'を使用するのではなく、foldlCを使用して、コンジットストリームを折り畳みます。

module Main where 

import Conduit 

readInts :: Source IO Int 
readInts = repeatWhileMC (read <$> getLine) (> 0) 

maxSumLength :: (Int, Int, Int) -> Int -> (Int, Int, Int) 
maxSumLength (m, s, l) x = (max m x, s+x, l+1) 

main :: IO() 
main = do 
    (m, s, n) <- runConduit (readInts =$= foldlC maxSumLength (0, 0, 0)) 
    putStrLn $ "max=" ++ show m ++ ", avg=" ++ show (fromIntegral s/fromIntegral n) 

このコードは、それらが作成されるようInt sが消費されるように、不純なreadIntsで純粋maxSumLengthインターリーブが、モジュール性を犠牲にすることなくなります。 readIntsストリームは、Intのストリームを必要とする他のプログラムで使用することができます。純粋なコードは、Intがどこから来ているかにはまだ気付きません。

1

最適ではありませんが、プログラムはほとんど動作しています。ここにいくつかの小さな修正とクリーンアップがあります。可能であれば、元のコードを保つようにしましたが、より良い解決策があるかもしれません。

最初に、定義されていないcAverageを使用しています。このエラーは簡単に修正できます。

average2パラメータは使用されていないため無意味です - 削除してください。

一部のletは、実際にそれらの変数を使用する分岐に移動することができます。

また、条件付きで2つの異なる再帰呼び出しを実行する代わりに、マイナーリファクタリングを実行して、新しいbigger値を条件付きで計算することもできます。しかし、max関数を使用する方が良いでしょう。

"より大きい"を "最大"、または "最大"または "最大"に変更することを検討してください。それは私にはうってつけです。

a6 :: Int -> Int -> Int -> IO() 
a6 bigger oldSum count = do 
c <- getLine 
let digit = read c :: Int 
if digit <= 0 
    then let average = fromIntegral oldSum/fromIntegral count :: Double 
      in putStrLn ("Bigger: " ++ show bigger ++ ", average: " ++ show average) 
    else let newBigger = if digit > bigger then digit else bigger 
       newSum = oldSum + digit 
      in a6 newBigger newSum (count+1) 
関連する問題