2017-01-13 3 views
2

私はCodinGameの課題のいくつかを行うことでHaskellを学ぼうと決めました(この質問は初心者レベルのものです)。それらのうちの1つは、任意の2つの値の間の最小の差について整数のリストを検索することを必要とする。尾を再帰的に調べることによるパフォーマンス

readHorses :: Int -> [Int] -> IO [Int] 
readHorses n h 
    | n < 1 = return h 
    | otherwise = do 
     l <- getLine 
     let hn = read l :: Int 
     readHorses (n - 1) (hn:h) 

findMinDiff :: [Int] -> Int -> Int 
findMinDiff h m 
    | (length h) < 2 = m 
    | (h!!1 - h!!0) < m = findMinDiff (tail h) (h!!1 - h!!0) 
    | otherwise   = findMinDiff (tail h) m 



main :: IO() 
main = do 
    hSetBuffering stdout NoBuffering -- DO NOT REMOVE 
    input_line <- getLine 
    let n = read input_line :: Int 
    hPrint stderr n 
    horses <- readHorses n [] 
    hPrint stderr "Read all horses" 
    print (findMinDiff (sort horses) 999999999) 
    return() 

大きな入力のためのこのタイムアウト(99999値:

(ns Solution 
    (:gen-class)) 

(defn smallest-difference [values] 
    (let [v (sort values)] 
    (loop [[h & t] v curr-min 999999] 
     (if (nil? t) curr-min 
     (let [dif (- (first t) h)] 
      (recur t (if (> curr-min dif) dif curr-min))))))) 

(defn -main [& args] 
    (let [horse-strengths (repeatedly (read) #(read))] 
     (let [answer (smallest-difference horse-strengths)] 
     (println answer)))) 

私は次のように、Haskellでは同じソリューションを実装しようとした:私は、以前にこれを行うことにより、Clojureの中でそれを解決してきました)どこClojureソリューションはしません。しかし彼らは私にかなり似ています。

少なくとも、「Read all horses」はタイムアウト前に出力されるため、値を読み込んでリストを構築するのは面倒なことです。

ハスケルバージョンをよりパフォーマンスの高いものにするにはどうすればよいですか?

+2

minDiff :: [Int] -> Maybe Int minDiff [] = Nothing minDiff h = Just (minimum (zipWith (-) (drop 1 h) h)) 
'長さh <2 '全体のリストを横断するよりよいエラー処理のため

。私は '[]'と '[_]'の代わりにパターンマッチングを使用したいと思います。 (最後のケースでは、 '(x1:x2:xs)'を使い、部分的な '!!'を避けます - これは速度を向上させませんが、もっと慣用的です) – chi

+0

ありがとう!私は間違いなくこのfindを示すfindMindDiffの実装例に感謝します。 – Orphid

答えて

5

リストのlengthをすべての再帰でfindMinDiffと計算しています。 lengthO(n)時間を要しますので、O(n)の代わりにO(n^2)時間を要します。あなたがところでpattern matching代わりのlength!!、およびtail

findMinDiff :: [Int] -> Int -> Int 
findMinDiff (h0 : [email protected](h1 : _)) m = 
    if h1 - h0 < m 
    then findMinDiff hs (h1 - h0) 
    else findMinDiff hs m 
findMinDiff _ m = m 
+3

'if ... then ...'else'は単なる式なので、関数呼び出しと最初の引数を取り除くことができます:' findMinDiff hs(h1 - h0 chepner

+1

@chepnerはい、この回答の目的は、長さとパターンマッチングのガード処理の違いを示すために、コードがOPとできるだけ似ていることです。もし私が「もしかしたら... else ...」という言葉を警備員に書いたのであれば、それは恐ろしいことではないでしょう。 – Cirdec

+0

私はコードが動作すると確信していますが、この構文 '(h0:hs @(h1:_))'は私を恐怖させる/混乱させます。このリンクからは、もっと冗長ではありますが、それに従う方が簡単な方法があるようです。または、その構文で私を助けてください。@何しているのですか?私は馬鹿だと思っても構いません。 – Orphid

3

と同じことを書くことができ

次のように、完全に代替実装を書くことができます。 (擬似コードは以下の)

は一つの要素

drop 1 h = [h1, h2, h3 ... 

計算の点別の違い

zipWith (-) (drop 1 h) h = [h1-h0, h2-h1, h3-h2, ... 

を削除リストに

h = [h0, h1, h2 ... 

を取るそして、最小を取ります。フルコード:

minDiff :: [Int] -> Int 
minDiff h = minimum (zipWith (-) (drop 1 h) h) 

これは空のリストでクラッシュすることに注意してください。一方で、ハックは不要です(9999999)。怠け者のおかげで、それは一定の空間でも動作します。

minDiff :: [Int] -> Int 
minDiff [] = error "minDiff: empty list" 
minDiff h = minimum (zipWith (-) (drop 1 h) h) 

あるいは、複数pedantically(しかし全体を尊重):

+0

はい - 私がチャレンジを完了した後、私は他人の解決策を見ることができました。 exlevanは最高の評価を受けているようで、 'minimum'と' zipWith'も使います。 – Orphid

+3

@Orphidライブラリ関数を知っているのは確かに助けになるかもしれませんが、エクササイズとしてはあまりにも多くの図書館の助けを借りなくてもこれを解決することを学ぶことが重要です。可能であれば、多くの初心者の練習では不必要で脆弱なコードにしかならないので、 'length、head、tail、!!'(そしておそらくガードも同様)を避けることをお勧めします。 Haskellでは、他のものとのパターンマッチングを好む方がはるかに優れています。 – chi

関連する問題