前のエントリでset型のメソッドをいくつか書いたけど、どれも元のデータを変更しないメソッドだった。
もちろんもとのデータを変更するメソッドもある。
addとremoveがそれ。
>>> s = set([1,2,3,4,5]) >>> s set([1, 2, 3, 4, 5]) >>> s.add(6) >>> s set([1, 2, 3, 4, 5, 6]) >>> s.remove(3) >>> s set([1, 2, 4, 5, 6])
takatoh's blog – Learning programming languages.
前のエントリでset型のメソッドをいくつか書いたけど、どれも元のデータを変更しないメソッドだった。
もちろんもとのデータを変更するメソッドもある。
addとremoveがそれ。
>>> s = set([1,2,3,4,5]) >>> s set([1, 2, 3, 4, 5]) >>> s.add(6) >>> s set([1, 2, 3, 4, 5, 6]) >>> s.remove(3) >>> s set([1, 2, 4, 5, 6])
Pythonには次の組み込み型がある。
文字列とユニコード文字列の違いはそのうち。リストはRubyで言う配列、辞書はハッシュだと思えばよさそう。タプルはHaskellにもあるタプルだよね。
boolにはTrueとFalseがある。
で、面白いのはsetという型があること。これはちょっと珍しいと思う。少なくともRubyにもPerlにもHaskellにも(標準では)ない。
set型はset()関数で作れる。
>>> s1 = set([1,2,3,4,5]) >>> s2 = set([4,5,6,7,8]) >>> s1 set([1, 2, 3, 4, 5]) >>> s2 set([8, 4, 5, 6, 7])
set型のメソッドをいくつか:
和集合
>>> s1.union(s2) set([1, 2, 3, 4, 5, 6, 7, 8])
共通集合
>>> s1.intersection(s2) set([4, 5])
差集合
>>> s1.difference(s2) set([1, 2, 3])
対照的差集合(どちらか一方に含まれるもの)
>>> s1.symmetric_difference(s2) set([1, 2, 3, 6, 7, 8])
演算子もある:
>>> s1 | s2 set([1, 2, 3, 4, 5, 6, 7, 8]) >>> s1 & s2 set([4, 5]) >>> s1 - s2 set([1, 2, 3]) >>> s1 ^ s2 set([1, 2, 3, 6, 7, 8])
上の操作では、s1、s2そのものは変化しない。
>>> s1 set([1, 2, 3, 4, 5]) >>> s2 set([8, 4, 5, 6, 7])
さて、ブログを書くのはおよそ3年ぶりというわけだけど、別に3年間何もやってなかったわけじゃなくて相変わらずRubyのスクリプトは書いていたし、Javascriptを触ったり、Perlをかじったりしていた。
で、最近はだいぶ前に買った「みんなのPython」(今見たら2006年の初版だった)を引っ張り出してPythonを触っている。バージョンは 2.7.3。そろそろ主流はPython3に移りつつあるのかもしれないけど、さくらインターネットのPythonも2.7.3だし、まぁ古くてもいいよねってことで。
とりあえずは定番のHello, worldから。
print "Hello, world."
実行結果。
^o^ > python hello.py Hello, world.
[amazonjs asin=”479733665X” locale=”JP” title=”みんなのPython”]
はてなダイアリーを3年近くもほったらかしにした末、衝動的にWordPressに移行した。
さくらインターネットのスタンダードに申し込んで、ドメインまでとった。金をかけるからには今度は続けるつもり、なんだけど、続くといいなぁ。
さて、サーバの設定自体はすんなりいった。事前にいくつかブログなど見たけど、実際にやってみるとチョー簡単。WordPressのインストールもさくらのサーバコントロールパネルのクイックインストールからあっという間にできた。参考にしたブログもほとんどいらなかったくらい。
cf. はてなダイアリーからWordPressへの移行手順 ― 科学と生活のイーハトーヴ
ただひとつ、はてなダイアリーのデータを移行するときに、ブログの日付の後のAMとかPMを削除しておかないと日付がおかしくなる、という情報があったので、それだけやっておいた。データをインポートした結果、おおむね大丈夫のようだ。
と思ったら、コードのインデントが全部なくなってる。ひえー、なんてこった。これ全部手で直さなきゃいけないのかなぁ。
[amazonjs asin=”4274067890″ locale=”JP” title=”プログラミングClojure”]
本屋で見かけたのでついうっかりと。Scalaの本は迷った挙句にやめたのに……大きさが手頃だからかも。
で,第1章 Getting Started と第2章 Clojureひとめぐり だけ読んでみた。以下メモ。
user=> #{}
#{}
user=> (def visitors (ref #{}))
#'user/visitors
user=> (dosync (alter visitors conj "Stu"))
#{"Stu"}
user=> {:Lisp "McCarty", :Clojure "Hickey"}
{:Lisp "McCarty", :Clojure "Hickey"}
user=> ({:Lisp "McCarty", :Clojure "Hickey"} :Clojure)
"Hickey"
user=> (:Clojure {:Lisp "McCarty", :Clojure "Hickey"})
"Hickey"
user=> (defn is-small? [number] (if (< number 100) "yes" (do (println "Saw a big number" number) "no"))) #'user/is-small? user=> (is-small? 101) Saw a big number 101 "no"
こないだのを参考にしてくれたらしい。
なんだか俺のコードよりもHaskellらしく見えるよ。
ところで,リンク先のコードだと検索するたびに suffix array というか suffix のリストを作っているように見える。俺の理解では,suffix array を利用するメリットというのは,suffix array を作るときには大量にメモリを必要とするけど検索するときには必要ない,ってことだと思うんだけど。
それはそれとして forever という関数を初めて知った。今度使ってみよう。
気がつけば12月も中旬だよ……。
少し前になるけど,「あとで試す」タグをつけといたやつをやってみる。これ↓:
cf. 簡単なWebサーチエンジンの作り方 – 加藤 和彦のブログ
具体的な手順はこっちのページで公開されている。
cf. http://www.osss.cs.tsukuba.ac.jp/kato/wiki/kato/index.php?Jikken-search-engine
さて,順にやってみよう。
与えられた文字列のsuffix arrayを作成するプログラムを作成せよ.
import Data.List
suffixArray :: String -> [Int]
suffixArray xs = map fst $ sort' $ zip [1..] $ init $ tails xs
where
sort' = sortBy (\a b -> compare (snd a) (snd b))
実行例:
*Main> suffixArray "abcbccab" [7,1,8,2,4,6,3,5]
与えられた文字列に対し,その部分文字列を入力し,部分文字列が出現する全位置を列挙する検索プログラムを作成せよ.(ヒント: suffix array上の2分探索を行う)
二分探索とはいうものの,検索対象の部分文字列の出現箇所すべてを列挙するには,中央の値(suffix)の右か左を単純に無視してしまうわけには行かない。場合によっては左右両方にあるかもしれないから。なので,まずは整理してみる。
これをコードにするとこうだ:
import Data.List
suffixArray :: String -> [Int]
suffixArray xs = map fst $ sort' $ zip [1..] $ init $ tails xs
where
sort' = sortBy (\a b -> compare (snd a) (snd b))
suffixOf :: String -> Int -> String
suffixOf s n = drop (n-1) s
search :: String -> [Int] -> String -> [Int]
search _ [] _ = []
search s ary sb = let n = (length ary) `div` 2
sfx = suffixOf s (ary !! n)
in
if sfx < sb then
search s (drop (n+1) ary) sb
else if sfx == sb then
(ary !! n) : search s (drop (n+1) ary) sb
else if isPrefixOf sb sfx then
(search s (take n ary) sb) ++ [ary !! n] ++ (search s (drop (n+1) ary) sb)
else
search s (take n ary) sb
実行例:
*Main> search "abcbccab" (suffixArray "abcbccab") "ab" [7,1]
指定された1個のHTMLテキストファイルをメモリ中に読み込んで1個の文字列とし,それに対する suffix array をメモリ中に作成し,ユーザから入力された文字列を検索して,入力文字列が出現する全位置を列挙するプログラムを作成せよ.
ファイルを読み込んで,searchを適用して,あとは適当にフォーマットして出力すればいいだけだ。ファイルは課題のページからリンクしてるこのページをダウンロードして使った(ファイル名決めうち)。
module Main where
import Data.List
import System.Environment ( getArgs )
-------------------------------------------------------------------------------
filename = "CodeConvTOC.doc.html"
main :: IO ()
main = do argv <- getArgs
contents <- readFile filename
substring <- return $ head argv
mapM_ (putStrLn . format contents) $ search contents (suffixArray contents) substring
format :: String -> Int -> String
format str pos = show pos ++ ": " ++ take 10 (suffixOf str pos)
-------------------------------------------------------------------------------
suffixArray :: String -> [Int]
suffixArray xs = map fst $ sort' $ zip [1..] $ init $ tails xs
where
sort' = sortBy (\a b -> compare (snd a) (snd b))
suffixOf :: String -> Int -> String
suffixOf s n = drop (n-1) s
search :: String -> [Int] -> String -> [Int]
search _ [] _ = []
search s ary sb = let n = (length ary) `div` 2
sfx = suffixOf s (ary !! n)
in
if sfx < sb then
search s (drop (n+1) ary) sb
else if sfx == sb then
(ary !! n) : search s (drop (n+1) ary) sb
else if isPrefixOf sb sfx then
(search s (take n ary) sb) ++ [ary !! n] ++ (search s (drop (n+1) ary) sb)
else
search s (take n ary) sb
-------------------------------------------------------------------------------
実行例:
^o^ >runhaskell suffixArray.hs File 10208: File Examp 1959: File Names 1664: File Names 2250: File Organ 1815: File Suffi 2422: Files</a>
ちょっと時間があいたけど続き。
以下の手順で,複数ファイルに対して全文検索を行うプログラムを作成せよ.
1. 指定された1個以上のm個のファイルをメモリ内で連結した長い文字列を作る.そのときにファイルの境 界に,テキストファイル中には通常は現れない文字(例えばヌル文字’\0’等)を入れ,検索時に複数ファイルを またいだ文字列にマッチしないようにしておく.
2. 1.で作った作った長い文字列中の文字位置から元のファイル名を得られるようにするための表を作る. 例えば,file1.html, file2.html, file3.htmlがそれぞれ100, 200, 300のファイルサイズをもつとき,[(“file 1.html”, 100), (“file2.html”, 200), (“file3.html”, 300)]というような表を作る(効率的な方法,プログ ラムしやすい方法を各自工夫せよ).
3. 課題1-3で作成したプログラムと,1.および2.で作ったデータを用いて,ユーザから入力された文字列を 検索し,入力文字列が出現するファイル名とファイル内の位置(ファイルの先頭から数えた文字数)を全て列 挙するプログラムを作成せよ.
課題1-3から変えたとこだけ:
main :: IO ()
main = do argv <- getArgs
files <- mapM readFile $ tail argv
let contents = concat $ intersperse "\0" files
let table = makeFileTable (tail argv) $ map length files
let substring = head argv
mapM_ (putStrLn . format table contents) $ search contents (suffixArray contents) substring
-------------------------------------------------------------------------------
format :: [(String, Int)] -> String -> Int -> String
format t str pos = let (f, p) = filePos t pos
in
f ++ ": " ++ show p ++ ": " ++ take 15 (suffixOf str pos)
-------------------------------------------------------------------------------
makeFileTable :: [String] -> [Int] -> [(String, Int)]
makeFileTable fs ls = zip fs $ snd $ mapAccumL (\a x -> (a+x+1, a)) 1 ls
filePos :: [(String, Int)] -> Int -> (String, Int)
filePos (f:[]) n = (fst f, n - snd f + 1)
filePos (f1:f2:fs) n | snd f2 < n = filePos (f2:fs) n
| otherwise = (fst f1, n - snd f1 + 1)
-------------------------------------------------------------------------------
元ファイルの表はファイル名とその開始位置をタプルにした。
実行例:
^o^ >runhaskell suffixArray.hs Intro CodeConvTOC.doc.html CodeCOnventions.doc.html CodeConvTOC.doc.html: 1063: Introduction</a CodeCOnventions.doc.html: 517: Introduction</h
今日はここまで。続きは明日……やれるといいなぁ。
ISBNを扱うのに ISBN Tools というライブラリを使っている。
なんでかっていうと ISBN_Tools.hyphenate_isbn13 っていうメソッドがあって,数字だけのISBNをハイフンの入ったISBNに変換してくれるのが使えると思ったからだ。
こんな感じ:
irb(main):006:0> ISBN_Tools.hyphenate_isbn13('9780672328848')
=> "978-0-6723-2884-8"
ところがこのメソッド,肝心の日本(グループ番号4)に対応してくれてない。
ソースを見たところ,data/ranges.dat にグループ番号と出版者記号の定義を追加すればいいみたいだ。
というわけでちょっと調べてみた。
日本国内のことなんだから日本のISBNを管理しているところへいけばいいんだろう,と思ったんだけど,その日本図書コード管理センターのサイトの中を探してみても規則で割り当ててるのか,どうもどこにも載ってない。
同じ不満をもっる人がネットのあちこちにいるみたいだな。どうなってんだ。
で,結局 ISBN の本家 International ISBN Agency のサイトでPDFを見つけた。これでいいんだよな。
これによると日本の出版者記号は下のような規則になっているようだ。
| 2ケタ | 00~19 |
| 3ケタ | 200~699 |
| 4ケタ | 7000~8499 |
| 5ケタ | 85000~89999 |
| 6ケタ | 900000~949999 |
| 7ケタ | 9500000~9999999 |
さて,ISBN Tools に戻ろう。
調べた成果を反映するには前述の data/ranges.dat に次の1行を追加すればいい。
4,00..19,200..699,7000..8499,85000..89999,900000..949999,9500000..9999999
カンマ区切りになってて,はじめの値がグループ番号,2つめ以降に出版者記号の連続する範囲を列挙している。これで日本の出版者にも対応するようになった。
irb(main):007:0> ISBN_Tools.hyphenate_isbn13('9784863540224')
=> "978-4-8635-4022-4"
10ケタのISBNでもできる。
irb(main):008:0> ISBN_Tools.hyphenate_isbn10('4873110238')
=> "4-87311-023-8"
import List steep xs = and $ zipWith (>) xs $ map sum $ tail $ tails xs
$ が多くてうっとうしいからがんばって無くしてみた。ついでに引数もなくなった。
import List
steep2 = and . s (zipWith (>)) sums
where
s f g x = f x (g x)
sums = map sum . tail . tails
なんかかえって解りにくいかも。
実行結果:
*Main> steep2 [32,16,8,4,2,1] True *Main> steep2 [31,16,8,4,2,1] False
自分用のメモ。
cf. Windows + Apache + FastCGI – h4yの日記
cf. WindowsでRuby on Rails その4 Ruby on RailsをFastCGIで動かす – 色々な事を忘れないよう忘備録と日記
FastCGIのダウンロードは公式サイトのダウンロードコーナーから。Apache のバージョンが 2.0.63 なので mod_fastcgi-2.4.2-AP20.dll をダウンロードした。これを mod_fastcgi.dll にリネームして,Apache の modules ディレクトリにコピー。
つぎは Ruby 側の準備。gem で fcgi をインストールしようとしたけど ruby のヘッダファイルがないって怒られる:
^o^ >gem install fcgi --remote Building native extensions. This could take a while... ERROR: Error installing fcgi: ERROR: Failed to build gem native extension. C:/usr/ruby/bin/ruby.exe extconf.rb install fcgi --remote can't find header files for ruby. Gem files will remain installed in C:/usr/ruby/lib/ruby/gems/1.8/gems/fcgi-0.8.7 for inspection. Results logged to C:/usr/ruby/lib/ruby/gems/1.8/gems/fcgi-0.8.7/ext/fcgi/gem_mak e.out
ググってみると,RubyForApache というのが見つかったので version 1.3.1 をダウンロード。なんか2005年って古いんだけど。
ファイルはインストーラなのでダブルクリックしてインストール。途中でインストールするコンポーネントを聞かれるので,mod_fastcgi 以外のコンポーネントのチェックをはずす。mod_rubyとmysql.soは今回はパス。
あと C:\Windows\System32\msvcp71.dll に書き込めないというメッセージが出るけど,これは無視して進める。
最後に Apache の設定。httpd.conf につぎの行を追加する。
LoadModule fastcgi_module modules/mod_fastcgi.dll AddHandler fastcgi-script .fcgi
Apache を再起動して終了。
テスト用のスクリプトはこんなの。
#! C:/usr/ruby/bin/ruby.exe require 'fcgi' FCGI.each_cgi do |cgi| print "Content-Type: text/plain\n\n" print "Hello world. This is FastCGI.\n" end
それと ExcecCGI を有効にしておく必要があるみたいなので .htaccess で設定する。
Options +ExecCGI