リストによる正方行列(つづき)

cf. 今日の一行 – リストによる正方行列処理

週をまたいでしまった。解答例もでてるけど。

問題3。これも内包表記で。

boxing :: [[a]] -> [[a]]
boxing m = [[m!!x!!y| x <- [0..l1], y <- [0..l1], x`div`l2 == i, y`div`l2 == j] | i <- [0..l3], j <- [0..l3]]
  where
    l1 = length m - 1
    l2 = (floor . sqrt . fromIntegral . length) m
    l3 = l2 -1

行列のサイズから n を求めるとこで型が合わずにはまった(sqrt(Floating a) => a -> a,必要なのは Int)。結局 floor を使った。

パターンの回転

パソコン甲子園というのを見つけた。
プログラミング部門の問題が公開されてるので(↓)ちょっとやってみよう。

正方行列つながりってことで問題01。

8文字×8行のパターンを右回りに90度、180度、270度回転させて出力し終了するプログラムを作成してください。

rotateMatrixR で右に90度回転させる。左に回転させる rotateMatrixL があるのは,勘違いしてさきに書いちゃったから。でも考えると右回転はちょっと面倒そうだな。これで正解かも。
main は手抜き。美しくない。

module Main (main) where

main :: IO ()
main = do cs <- getContents >>= return . lines
          putStrLn "90"
          showMatrix $ rotateMatrixR cs
          putStrLn "180"
          showMatrix $ rotateMatrixR $ rotateMatrixR cs
          putStrLn "270"
          showMatrix $ rotateMatrixR $ rotateMatrixR $ rotateMatrixR cs

rotateMatrixL :: [[a]] -> [[a]]
rotateMatrixL [xs] = [[x]| x <- reverse xs]
rotateMatrixL (xs:xss) = zipWith (:) (reverse xs) (rotateMatrixL xss)

rotateMatrixR :: [[a]] -> [[a]]
rotateMatrixR = reverse . rotateMatrixL . reverse

showMatrix :: [String] -> IO ()
showMatrix = putStr . unlines

実行例。

D:\>type input01.txt
#*******
#*******
#*******
#*******
#*******
#*******
#*******
########
D:\>runhaskell problem01.hs < input01.txt
90
########
#*******
#*******
#*******
#*******
#*******
#*******
#*******
180
########
*******#
*******#
*******#
*******#
*******#
*******#
*******#
270
*******#
*******#
*******#
*******#
*******#
*******#
*******#
########

追記:
右回転の方が簡単だった……orz

rotateMatrixR' :: [[a]] -> [[a]]
rotateMatrixR' = transpose . reverse
*Main> showMatrix sampleMatrix
#*******
#*******
#*******
#*******
#*******
#*******
#*******
########
*Main> showMatrix $ rotateMatrixR' sampleMatrix
########
#*******
#*******
#*******
#*******
#*******
#*******
#*******
*Main> showMatrix $ rotateMatrixR' $ rotateMatrixR' sampleMatrix
########
*******#
*******#
*******#
*******#
*******#
*******#
*******#

さらに追記:
ちがう!左回転も同じくらい簡単だ。

rotateMatrixL' :: [[a]] -> [[a]]
rotateMatrixL' = reverse . transpose

リストによる正方行列

cf. 今日の一行 – リストによる正方行列処理

問題1。

matrix n = [[x+(n^2*y) | x <- [1..n^2]] | y <- [0..n^2-1]]
*Main> matrix 2
[[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]]
*Main> matrix 3
[[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,32,33,34,35,36],[37,38,39,40,41,42,43,44,45],[46,47,48,49,50,51,52,5
3,54],[55,56,57,58,59,60,61,62,63],[64,65,66,67,68,69,70,71,72],[73,74,75,76,77,
78,79,80,81]]

問題2。
Data.Listtranspose そのままでは芸がないので。

transpose' m = [[m!!x!!y| x <- [0..l]] | y <- [0..l]]
  where
    l = length m - 1
*Main> transpose' $ matrix 2
[[1,5,9,13],[2,6,10,14],[3,7,11,15],[4,8,12,16]]

問題3はあとで。

追記:
問題2の別解。foldr を使う。これなら正方行列でなくてもOK。

transpose'2 m = foldr (\a b -> zipWith (:) a b) (replicate (length m) []) m
*Main> transpose'2 $ matrix 2
[[1,5,9,13],[2,6,10,14],[3,7,11,15],[4,8,12,16]]

クラスはよくてもモジュールは?

いままで気にしたことなんて無かったけど……

cf. Matzにっき – jijixi’s diary – Ruby の文法的欠陥

cf. じじぃの日記、ツッコミ可 – Ruby の文法的欠陥

jijixiさんのいう”欠陥”(というと言いすぎのような気もする,といっているけど)というのは

『新しいクラスを作ってるつもりで、知らないうちに既存のクラスを上書きする可能性がある』

で,これに対するまつもとさんの対策が

* ちゃんと名前空間を分離する

* クラスを定義する時にはスーパークラスを明示する

2つ目の方は,クラスを定義するときに指定したスーパークラスが合わないとエラーになるってことだな。

D:\>irb
irb(main):001:0> class Foo; end
=> nil
irb(main):002:0> class Bar; end
=> nil
irb(main):003:0> class Baz < Foo; end
=> nil
irb(main):004:0> class Baz < Bar; end
TypeError: superclass mismatch for class Baz
from (irb):4
from :0

そうは言っても class Foo < Object って書いてるのは見たこと無いけど。

さて,クラスはいいとしてもモジュールはどうだろう。

スーパークラスの明示みたいなものはないし,トップレベルで定義されたモジュールでは名前空間の分離もできない。

……ってことは,気をつけるしかないのか。

ちなみに,素の状態で定義されているモジュールは

D:\>type modules.rb
ObjectSpace.each_object(Object){|o| puts o.name if o.class == Module}
D:\>ruby modules.rb
Marshal
ObjectSpace
GC
Math
Process::Sys
Process::GID
Process::UID
Process
Signal
File::Constants
FileTest
Errno
Precision
Enumerable
Comparable
Kernel

意外に少ない。

ファイル入力か標準入力

引数が与えられたらそれをファイル名と見なしてファイルを読み込み,与えられなければ標準入力から読み込む。いわゆるフィルタとしても働くプログラム。こんな感じでいいか?

module Main (main) where

import System

main :: IO ()
main = do args <- getArgs
          contents <- if (not.null) args then readFile $ head args else getContents
          putStr contents

引数にファイル名を指定

D:\>runghc catFile.hs hello.txt
Hello, Haskell.

標準入力から

D:\>runghc catFile.hs < hello.txt
Hello, Haskell.

パイプ経由

D:\>type hello.txt | runghc catFile.hs
Hello, Haskell.

loop の列挙

cf. 今日の一行 – loopの列挙

trail はすでにたどったノードのリスト p と次にたどるノードの候補 q を受け取って,全ての経路を列挙する。「次のノード」がスタートと同じならそこでそのループは終わり。違うなら再帰的にノードをたどる。
各ノードは比較さえできればいいので Eq a にした。入出力の関係で結局は文字列になってるけど。

module Main (main) where

import Data.List (intersperse)
import System (getArgs)

trail :: (Eq a) => [a] -> [a] -> [[a]]
trail p q = concat $ map trail' q
  where
    trail' q1 | head p == q1 = [ p ++ [q1] ]
              | otherwise = trail (p ++ [q1]) $ filter (q1/=) q

trailLoop :: (Eq a) => a -> [a] -> [[a]]
trailLoop s = trail [s]

enumLoops :: (Eq a) => [a] -> [[a]]
enumLoops nodes = concat $ map (flip trailLoop nodes) nodes

showLoop :: [String] -> String
showLoop = concat . intersperse " -> "

main :: IO ()
main = do nodes <- getArgs
          mapM_ (putStrLn . showLoop) $ enumLoops nodes

実行例

D:\>runghc enumLoops.hs 1 2 3
1 -> 1
1 -> 2 -> 1
1 -> 2 -> 3 -> 1
1 -> 3 -> 1
1 -> 3 -> 2 -> 1
2 -> 1 -> 2
2 -> 1 -> 3 -> 2
2 -> 2
2 -> 3 -> 1 -> 2
2 -> 3 -> 2
3 -> 1 -> 2 -> 3
3 -> 1 -> 3
3 -> 2 -> 1 -> 3
3 -> 2 -> 3
3 -> 3

ノードが1つ増えるとループはぐっと増える。

D:\>runghc enumLoops.hs 1 2 3 4
1 -> 1
1 -> 2 -> 1
1 -> 2 -> 3 -> 1
1 -> 2 -> 3 -> 4 -> 1
1 -> 2 -> 4 -> 1
1 -> 2 -> 4 -> 3 -> 1
1 -> 3 -> 1
1 -> 3 -> 2 -> 1
1 -> 3 -> 2 -> 4 -> 1
1 -> 3 -> 4 -> 1
1 -> 3 -> 4 -> 2 -> 1
1 -> 4 -> 1
1 -> 4 -> 2 -> 1
1 -> 4 -> 2 -> 3 -> 1
1 -> 4 -> 3 -> 1
1 -> 4 -> 3 -> 2 -> 1
2 -> 1 -> 2
2 -> 1 -> 3 -> 2
2 -> 1 -> 3 -> 4 -> 2
2 -> 1 -> 4 -> 2
2 -> 1 -> 4 -> 3 -> 2
2 -> 2
2 -> 3 -> 1 -> 2
2 -> 3 -> 1 -> 4 -> 2
2 -> 3 -> 2
2 -> 3 -> 4 -> 1 -> 2
2 -> 3 -> 4 -> 2
2 -> 4 -> 1 -> 2
2 -> 4 -> 1 -> 3 -> 2
2 -> 4 -> 2
2 -> 4 -> 3 -> 1 -> 2
2 -> 4 -> 3 -> 2
3 -> 1 -> 2 -> 3
3 -> 1 -> 2 -> 4 -> 3
3 -> 1 -> 3
3 -> 1 -> 4 -> 2 -> 3
3 -> 1 -> 4 -> 3
3 -> 2 -> 1 -> 3
3 -> 2 -> 1 -> 4 -> 3
3 -> 2 -> 3
3 -> 2 -> 4 -> 1 -> 3
3 -> 2 -> 4 -> 3
3 -> 3
3 -> 4 -> 1 -> 2 -> 3
3 -> 4 -> 1 -> 3
3 -> 4 -> 2 -> 1 -> 3
3 -> 4 -> 2 -> 3
3 -> 4 -> 3
4 -> 1 -> 2 -> 3 -> 4
4 -> 1 -> 2 -> 4
4 -> 1 -> 3 -> 2 -> 4
4 -> 1 -> 3 -> 4
4 -> 1 -> 4
4 -> 2 -> 1 -> 3 -> 4
4 -> 2 -> 1 -> 4
4 -> 2 -> 3 -> 1 -> 4
4 -> 2 -> 3 -> 4
4 -> 2 -> 4
4 -> 3 -> 1 -> 2 -> 4
4 -> 3 -> 1 -> 4
4 -> 3 -> 2 -> 1 -> 4
4 -> 3 -> 2 -> 4
4 -> 3 -> 4
4 -> 4

文字列の中央詰めを length を使わずに短く書けるだろうか?

via 毎日Haskell – 2006-12-28 文字列の左詰、右詰
 cf. desumasuの日記 – Rubyの文字列操作関数をHaskellで定義する

length を使わずに書けたけど,短いとは言い難い。
ljust(左詰)と rjust(右詰)が定義されてるとして

center :: Int -> String -> String
center 0 [] = ""
center n xs = f (ljust n xs) (rjust n xs)
  where
    f l r | l == r = l
          | otherwise = g l (tail r ++ " ")
    g l r | l == r = l
          | otherwise = f " " ++ take (n-1) l) r

id:desumasu さんのと引数の順番が違うのは,この方が Haskell っぽいから(気のせい?)。2行目がないと,空文字列を0文字に中央詰めする場合にエラーになる。

実行例。

*Main> center 8 "abc"
"  abc   "
*Main> center 8 "abcd"
"  abcd  "
*Main> center 3 "abcd"
"abcd"
*Main> center 3 ""
"   "
*Main> center 0 ""
""

HUnit を使ったテスト(id:desumasuさんのコードを改変)。

import Test.HUnit

testCenter = test [
  "test1" ~: " hoge " ~=? center 8 "hoge" ,
  "test2" ~: "hoge" ~=? center 1 "hoge" ,
  "test3" ~: " hoge " ~=? center 7 "hoge" ,
  "empty1" ~: "" ~=? center 0 "" ,
  "empty2" ~: " " ~=? center 2 ""
]
*Main> runTestTT testCenter
Cases: 5  Tried: 5  Errors: 0  Failures: 0

lighttpd (for Windows) に触ってみた(2)

昨日(id:takatoh:20070111:lighttpd) のつづき。

ファイルのリスト( mod_dirlisting )

URL がディレクトリを指して,かつインデックスファイルが見つからない場合に,ディレクトリ内容のリストを表示する機能。mod_dirlisting モジュールは server.modules で指定する必要もなくデフォルトで読み込まれるので,機能を有効にするだけでいい。

dir-listing.activate       = "enable"

もし,特定のディレクトリだけリストを表示したいなら次のようにする(これは C:\lighttpd\doc\dirlisting.txt に載っている例)。

$HTTP["url"] =~ "^/download($|/)" {
dir-listing.activate = "enable"
}

エイリアス( mod_alias )

mod_alias を読み込んで

"mod_alias",

エイリアスのリストを指定。lighttpd.conf に記述がないので追加する。

alias.url = ( "/cgi-bin/" => "C:/lighttpd/cgi-bin/" )

バーチャルホスト( mod_simple_vhost )

バーチャルホスト関連のモジュールには mod_evhost というのもあるけど,ここでは mod_simple_vhost を使う(名前ベース)。まずはモジュールの読み込み。

"mod_simple_vhost",

でもって,次のように設定する。これがデフォルトのバーチャルホストになる。

simple-vhost.server-root   = "C:/lighttpd/servers/"
simple-vhost.default-host  = "vhost1"
simple-vhost.document-root = "/pages/"

simple-vhost.server-root で指定したディレクトリ以下にバーチャルホストと同名のディレクトリを用意する。ほかの場所に作ってもダメなようだ。

で,この例では vhost1 がバーチャルホスト名,C:/lighttpd/servers/vhost1/pages/ がそのドキュメントルートだ。

複数のバーチャルホストをたてるには

$HTTP["host"] == "vhost2" {
server.document-root = "C:/lighttpd/servers/vhost2/pages/"
}

のように $HTTP[“host”] の値で場合分けする。

元々のホスト--昨日の例でいえば http://localhost:81/ でアクセスしていたホスト--は無効になるようだ。

lighttpd (for Windows) に触ってみた

ので,メモ。

cf. http://www.lighttpd.net/ (公式サイト)

cf. ぱるも日記 – Windows で lighttpd を使う

ダウンロード

↓ダウンロードはここから。バージョンは 1.4.11。

http://blogs.windowsnetworking.com/wnadmin/2006/09/22/lighttpd-web-server/

公式サイトにはWindows用バイナリ配布サイトへのリンクがあるけど,どういう訳か接続できない。

で,ここの人がミラーしてるってことらしい。最新バージョンではないけど気にしないことにする。

インストール

インストールは簡単。ダウンロードしたインストーラを実行するだけ。

インストール先を聞いてこないので,C:\lighttpd に固定のようだ。

設定

設定ファイルは,C:\lighttpd\etc\lighttpd.conf。

ぱるも日記 – Windows で lighttpd を使うを参考に。あと,付属のマニュアル(英語)をちょっとだけ参照。

まずは読み込むモジュール。CGI を使えるように,mod_cgi モジュールを読み込む。コメントをはずせばいい(29行目)。

"mod_cgi",

ドキュメントルート。これはデフォルトのまま。

server.document-root        = "C:/lighttpd/htdocs/"

URLにファイル名が省略されたときのインデックスファイルに index.cgi を追加(46行目)。

index-file.names            = ( "index.cgi", "index.php", "index.html",

ポート(138行目)。デフォルトは 80 だけど Apache が使ってるので変更。

server.port                = 81

CGIスクリプトの拡張子と実行プログラムを指定(219行目~)。

cgi.assign                 = ( ".pl"  => "C:/usr/perl/bin/perl",
".rb"  => "C:/usr/ruby/bin/ruby",
".cgi" => "C:/usr/ruby/bin/ruby" )

これで lighttpd を再起動すれば,設定が有効になる。

起動・停止はスタートメニューかアイコンでできる。けど,リスタートが無いのがちょっと不便。

ユーザーディレクトリ

ユーザーディレクトリも使える。まずは mod_userdir を有効化。

"mod_userdir",

パスの設定。lighttpd.conf にデフォルトの記述がないので一番最後に追加した。

userdir.basepath = "D:/www/lighttpd_users/"
userdir.path     = "htdocs"

これで URL とパスの関係は次のようになる。

Apache の .htaccess に相当するものは無いのかな。

つづき –> id:takatoh:20070112:lighttpd