« 2012年1月 | トップページ | 2012年4月 »

2012年2月

2012年2月29日 (水)

ブログの内容は鵜呑みにしない方がいいですよ(笑)

どうして中途半端な知識しかもっていない人に限って、ドヤ顔で人にその知識 を教えたがるんだろう?

明らかに Ruby 初心者の人が、解説口調で Ruby のコードの説明をしてるブロ グを見つけたんですけど…説明が微妙に間違ってたり、ブログに書かれてるコー ドが妙に冗長だったり…。

 

例えばこんな感じ…。

 

1000回繰り返したかったら

1000.times do |i|
p i
end

これは1から1000までを単に書き出すプログラムになります。

 

以前このブログでも書きましたが、この場合、"i" には 0 〜 999 の値が入る ので実際は 0 から 999 が書き出されます。

プログラミングの経験がある人ならご存知でしょうが、「0 から始まるか、1 から始まるか」をはっきり認識しておかないと、変なバグに悩まされることが あります。

 

また、フィボナッチ数列を表示し続けるコードが提示してあるのですが、無駄 な処理を含んだ冗長なコードです。

 

フィボナッチの数列は前の2つの項さえ分かっていれば新しい項が生成できるので、 変数を3つ用意します。
そして変数に初期値を入れておきます。

a=1
b=2
c=0

aは第1項目、bは第2項目、cは第3項目です。
そしてloop文を制御するカウンターも必要です。

i=3

3項目から求めるのでカウンターは3からスタートです。
事前準備はこれでおk フィボナッチ数列だけをRubyで書くと

a=1
b=2
c=0
i=3
sum=2
loop do
  if i%3==0 then
    p c=a+b
  end
  if i%3==1 then
    p a=c+b
  end
  if i%3==2 then
    p b=c+a
  end
  i=i+1
end

こんな感じになります。

 

なぜか、数値を格納する変数が3個も準備され、さらに loop文を制御するカウンターまで…。

 

ネットで少し調べれば、このコードよりも効率的なコードはすぐに見つかると 思います。

私なら次のようにしますね。

a = 1
b = 1
loop do
  p a
  a, b = a + b, a
end

 

自分でも同じような間違いを過去に(もしかしたら現在も)犯してるのかな?
ブログはいろいろな人の目に留まるから、気をつけないと…。

もし私のブログの内容に間違いを見つけた人がいらっしゃったら、すぐに教え てください。

また、ブログを読む人は一つのブログの内容を鵜呑みにするのではなく、いく つかのブログを読み比べてみたほうがいいと思います。

なお、この記事は自戒のために書いたので、参考のために挙げたブログへのリ ンクやトラックバックは行なっていません。

HTML generated by org-mode 6.33x in emacs 23

2012年2月24日 (金)

リストモナド

こちらのブログ経由でこの記事にたどり着きました。
一つめのコードは「指定した文字列の大文字小文字の組み合わせ全パターンの リストを取得する」というコードなんですが…確かに少し冗長な気がします。
私なら次のようなコードを書くかな…。

import Data.Char

getAllUpperLowerPattern :: String -> [String]
getAllUpperLowerPattern cs = foldr f [""] cs
  where f c css = concat [[toUpper c : cs, toLower c : cs] | cs <- css]

 

と思った後にこちらのブログをよく読んだら、リストモナドに「sequence」と いう関数があったんですね。
この関数を使えばコードがさらに簡潔になります。

import Data.Char

getAllUpperLowerPattern :: String -> [String]
getAllUpperLowerPattern cs = sequence [[toUpper c, toLower c] | c <- cs]

 

やっぱり、もっとモナドの勉強をしなければいけませんね…。

 

 

Date: 2012-02-24 12:48:56 JST

HTML generated by org-mode 6.33x in emacs 23

2012年2月16日 (木)

Codeforces 35

最近、「Project Euler」を休んで、「Codeforces」の過去問を Haskell で解いて遊んでます。
ずっと「モナド」とか「IO アクション」から逃げてたんですけど、「Codeforces」では「入力」と「出力」が決まってるので「IO」の練習になるかな…と思いまして。

今回、この問題を解いてみました。
一般的な手続型言語では配列にデータを入れておいて、後はスワップを繰り返せばいいのでしょうけど、Haskell だとリストや配列のスワップはコストが高いよな…と思って、次のようなコードを書きました。

test :: String -> [String] -> Int
test "1" xs = loop 1 0 0 xs
test "2" xs = loop 0 1 0 xs
test "3" xs = loop 0 0 1 xs

loop :: Int -> Int -> Int -> [String] -> Int
loop 1 _ _ [] = 1
loop _ 1 _ [] = 2
loop _ _ 1 [] = 3
loop a b c (x1 : x2 : xs)
  | (x1, x2) == ("1", "2") || (x1, x2) == ("2", "1") = loop b a c xs
  | (x1, x2) == ("1", "3") || (x1, x2) == ("3", "1") = loop c b a xs
  | (x1, x2) == ("2", "3") || (x1, x2) == ("3", "2") = loop a c b xs

main :: IO ()
main = do input <- readFile "input.txt"
          let ([i], xs) = splitAt 1 $ words input
          writeFile "output.txt" $ show $ test i xs

一応、結果は「Accepted」でしたが、力技で解いた感があってあまり美しいと言えません。

 

今後の参考にと、他の人の解放を見ていたら、次のようなコードを見つけました。私とはちょっと発想が違っています。

import IO

gao :: [Int] -> Int
gao [a] = a
gao (a:b:c:d) = gao $ (if a == b then c else if a == c then b else a):d

main = do
        hi <- openFile "input.txt" ReadMode
        ho <- openFile "output.txt" WriteMode
        hGetContents hi >>= hPutStr ho . show . gao . map read . words
        hClose ho

このコードを参考に(パクッて?)、自分の好きなスタイルでコードを書いてみました。 (私は "if 〜 then 〜 else 〜 " があまり好きではありません。特に参考にしたコードのように if が連なっていると、直感的に分りにくい気がします)

test :: String -> [String] -> String
test i (a : b : xs)
  | i == a    = test b xs
  | i == b    = test a xs
  | otherwise = test i xs
test i [] = i

main :: IO ()
main = do input <- readFile "input.txt"
          let (i : xs) = words input
          writeFile "output.txt" $ test i xs

最初に自分で書いたコードより、こちらのコードのほうがずっと良い気がします。

Date: 2012-02-16 11:48:22 JST

HTML generated by org-mode 6.33x in emacs 23

« 2012年1月 | トップページ | 2012年4月 »

2016年7月
          1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31            
フォト

最近のトラックバック

無料ブログはココログ