2012/03/30

2012/03/30

2012/03/29

2012/03/29

日記

ブログ書きたいんだけどなかなか気分的な余裕がない。いや気分的な余裕はあるか。まとまった時間がない。いや時間もそこそこあるな。Land of Lispの和訳すごく楽しみ。そういえば僕は会社で「準備と段取りと根回し」「人生の楽しみ方」を学んでいます。出張続きであんまりプログラム書いてないです。でも職場すごく楽しいです。おやすみ。

2012/03/25

2012/03/25

日記

忙しいと現実逃避が多くなるなー。主にネットへの逃避ですね。ネットサーフィンとかSNS系。いかんですね。逃避なのかようわからんですが。読書やプログラミングよりは非生産的だというだけか。

ここんとこ出張が多かったので疲れが溜まってるらしい。今日も髪を切りに行った以外はずっと寝てましたがまだ眠い。

ところで。まだ日記に書いてなかったけど2012/03/14(水)に結婚しました。式・披露宴は5月です。楽しみだけど忙しい。

2012/03/24

日記

奄美大島に出張してきました。けいはんうめぇー!プロペラ機揺れすぎ怖ぇー!出張続きだったので疲れがたまりました。でも出張もなかなか楽しい。最近日記書けてないのでぼちぼち復活したい。

2012/03/10

2012/03/10

リスト ("*" (2 3 4) (a b) "!" (8 9 0)) から文字列 "*2a!8", "*2a!9", "*2a!0", "*2b!8" ...を作る

2chのLisp Schemeスレに掲題の問題が出てた。
235 :デフォルトの名無しさん:2012/03/09(金) 15:21:07.33
Allegro Common Lisp (ACL) の試用版を使っています 
(list "*" '(2 3 4) '(a b) "!" '(8 9 0)) 
こういう文字列とリストからなるリストをもとに 
*2a!8 
*2a!9 
*2a!0 
*2b!8 
*2b!9 
*2b!0 
*3a!8 
... 
と続くものを出力したいです(文字列とリストは0回以上ランダムに表れます) 
リストについては命令型言語でいうところの 
for (...) 
 for (...) 
のような処理をすることになりそうですが 
どこから手をつければよいかわかりません 
おそらくmapcarを使ってリストを作り直す必要があります? 
どうすればよいですか 
この問題って、対象のリスト
(list "*" '(2 3 4) '(a b) "!" '(8 9 0))
の文字列も要素一個の集合ってことにして
(list '("*") '(2 3 4) '(a b) '("!") '(8 9 0))
であると見なして、集合の集合と考えちゃう。するとこれらの集合に対してSQLで言うところのcross joinの結果を出せばいいんじゃないかという気がする。調べてみたらcross joinってデカルト積って言うんですって。そこで、デカルト積を出すにはどうしたらいいのかなーと探してみたらgaucheのutil.combinationsにcartesian-productってのが組み込みであった。
ちょっとズルい気もするけど、それっぽい結果になった。gaucheで書きました。
(use util.combinations)

(let* ((ls (list "*" '(2 3 4) '(a b) "!" '(8 9 0)))
       (ls (map (^x (if (list? x)
                        x
                        (list x))) ls)))
  (for-each (^s (write s)(newline))
            (map (apply$ string-append)
                 (map (map$ x->string)
                      (cartesian-product ls)))))

;; "*2a!8"
;; "*2a!9"
;; "*2a!0"
;; "*2b!8"
;; "*2b!9"
;; "*2b!0"
;; "*3a!8"
;; "*3a!9"
;; "*3a!0"
;; "*3b!8"
;; "*3b!9"
;; "*3b!0"
;; "*4a!8"
;; "*4a!9"
;; "*4a!0"
;; "*4b!8"
;; "*4b!9"
;; "*4b!0"


追記

せっかくなのでちょっと見やすく
(use util.combinations)

(define (print-list ls)
  (for-each (^s (write s)(newline)) ls))

(define (list->set-of-set ls)
  (map (^x (if (list? x)
               x
               (list x))) ls))

(define (cartesian-product-string set-of-set)
  (map (apply$ string-append)
       (map (map$ x->string)
            (cartesian-product set-of-set))))

(define (main ls)
  (print-list
   (cartesian-product-string
    (list->set-of-set ls))))

(main (list "*" '(2 3 4) '(a b) "!" '(8 9 0)))

;; "*2a!8"
;; "*2a!9"
;; "*2a!0"
;; "*2b!8"
;; "*2b!9"
;; "*2b!0"
;; "*3a!8"
;; "*3a!9"
;; "*3a!0"
;; "*3b!8"
;; "*3b!9"
;; "*3b!0"
;; "*4a!8"
;; "*4a!9"
;; "*4a!0"
;; "*4b!8"
;; "*4b!9"
;; "*4b!0"

プログラミングGauche

2012/03/09

2012/03/09

csvをsqlにする(insert, update)

検索すればいくらでも出てくるようなものをあえて作りました。csvをinsert文、update文に変換するコマンド。

昨日のこれと合わせると結構使えるかも。
例えばこういうCSVを
$ cat sample.csv 
aaa,bbb,ccc,ddd,eee
1,2,3,4,5
10,20,30,40,50
100,200,300,400,500
csv2sqlに渡すと
$ csv2sql sample.csv 
INSERT INTO sample (aaa , bbb , ccc , ddd , eee) VALUES ('1' , '2' , '3' , '4' , '5')
INSERT INTO sample (aaa , bbb , ccc , ddd , eee) VALUES ('10' , '20' , '30' , '40' , '50')
INSERT INTO sample (aaa , bbb , ccc , ddd , eee) VALUES ('100' , '200' , '300' , '400' , '500')

$ csv2sql -k aaa sample.csv
UPDATE sample SET aaa='1', bbb='2', ccc='3', ddd='4', eee='5' WHERE aaa='1'
UPDATE sample SET aaa='10', bbb='20', ccc='30', ddd='40', eee='50' WHERE aaa='10'
UPDATE sample SET aaa='100', bbb='200', ccc='300', ddd='400', eee='500' WHERE aaa='100'

$ csv2sql -k "aaa,ccc" sample.csv
UPDATE sample SET aaa='1', bbb='2', ccc='3', ddd='4', eee='5' WHERE aaa='1' AND ccc='3'
UPDATE sample SET aaa='10', bbb='20', ccc='30', ddd='40', eee='50' WHERE aaa='10' AND ccc='30'
UPDATE sample SET aaa='100', bbb='200', ccc='300', ddd='400', eee='500' WHERE aaa='100' AND ccc='300'
こんな感じ。CSVファイル名がTable名に、CSVのheader行がカラム名に。update文でキーまで更新してることに今ごろ気づいたけど気にしない。

ソース

Gaucheで書きました。ソースはこちら。
#!/usr/local/bin/gosh
(use srfi-1)
(use text.csv)
(use file.util)
(use util.list)
(use gauche.parseopt)
(define (usage)
(print "Usage: csv2sql [options ...] <csv-file>")
(print " h|help - print this usage")
(print " k|keys - primary key column names (delimiter=,)")
(print " d|delimitter - (default=,)")
(print " n|tablename")
(print " t|tsv")
(print)
(print "SQL query")
(print " default => insert")
(print " -k|keys => update")
(print "Table name and Column name")
(print " Table name: <csv-file> name without extentions")
(print " Column name: header row column names")
(exit 1))
(define (csv->list file :optional (delim #\,))
(port->list (make-csv-reader delim)
(if file
(open-input-file file)
(current-input-port))))
(define (csv->sql csv table-name . key-col-names)
(if (null? csv)
'()
(let ((header (car csv))
(csv (cdr csv)))
(if (null? key-col-names)
(csv->insert-str csv table-name header)
(apply csv->update-str csv table-name header key-col-names)))))
(define (path->filename-without-ext path)
(receive (dir filename ext)
(decompose-path path)
filename))
(define (keys-str->list keys-str)
(string-split keys-str #\,))
(define (csv->insert-str csv table-name column-names)
(define (make-values row)
(intersperse "," (map (pa$ format "'~a'") row)))
(map (^[row]
(format "INSERT INTO ~a ~a VALUES ~a;"
table-name
(intersperse "," column-names)
(make-values row)))
csv))
(define (csv->update-str csv table-name column-names . key-col-names)
(define (make-set row)
(apply string-append
(intersperse
", "
(fold-right (^[colname val acc]
(if (member colname key-col-names)
acc
(cons (format "~a='~a'" colname val) acc)))
'() column-names row))))
(define (make-where row)
(apply string-append
(intersperse
" AND "
(map (^[key-col-name]
(let1 idx (list-index (pa$ equal? key-col-name)
column-names)
(format "~a='~a'" key-col-name (~ row idx))))
key-col-names))))
(map (^[row]
(format "UPDATE ~a SET ~a WHERE ~a;"
table-name
(make-set row)
(make-where row)))
csv))
(define (main args)
(let-args (cdr args)
((help "h|help" => usage)
(keys "k|keys=s")
(delim "d|delimitter=s" #\,)
(tsv? "t|tsv")
(tablename "n|tablename=s")
(else (opt . _)
(print "Unknown option : " opt)
(usage))
. rest)
(let ((csv (csv->list (and (not (null? rest))(car rest))
(if tsv? #\tab delim)))
(filename (cond ((and (null? rest) tablename) tablename)
((null? rest) "<TableName>")
(else (path->filename-without-ext (car rest)))))
(keys (if keys (keys-str->list keys) '())))
(for-each print (apply csv->sql csv filename keys)))))
view raw csv2sql.scm hosted with ❤ by GitHub


追記

update文でキーまで更新してることに今ごろ気づいたけど気にしない。 

対応した。


2012/03/08

2012/03/08

shell scriptでオプション引数を除いた引数を得る

getoptsした後のOPTIND - 1分shiftすればよかったのかー。というかOPTINDなるものを知らなんだ。
#!/bin/sh

while getopts abcdef:g:h: OPT
do
        echo $OPT
done

echo $OPTIND
shift `expr "$OPTIND" - 1`
echo "$@"
$ ~/temp % ./cmdargs -a -b -c -d -e -f hoge -g fuga -h piyo a b c d
a
b
c
d
e
f
g
h
12
a b c d
$ ~/temp % ./cmdargs -a -b -c -f hoge -g fuga -h piyo a b c d 
a
b
c
f
g
h
10
a b c d


UNIXという考え方―その設計思想と哲学

shellで相対パスを絶対パスに

もっと簡単な方法はないのかね。
$ abp ~/bin
/home/valvallow/bin
#!/bin/sh
# http://dokonoumanohone.blog47.fc2.com/blog-entry-2.html
for ARG in "$@"
do
echo $(cd $(dirname "$ARG") && pwd)/$(basename "$ARG")
done
view raw abp.sh hosted with ❤ by GitHub


追記

@kikuchan98先生に教えてもらいました!

UNIXという考え方―その設計思想と哲学

1行目無視

1行目無視。sedで行指定で削除できんのか。
$ cat hoge.txt | sed '1d'

追記

これまた@kikuchan98先生に教えてもらいました!
$ cat hoge.txt | tail -n +2
UNIXという考え方―その設計思想と哲学

csvをmapping

こういうCSVがあるとき
$ cat sample.csv
a,b,c
1,2,3
10,20,30
100,200,300
1,2,3
10,20,30
100,200,300
1,2,3
10,20,30
100,200,300
1,2,3
10,20,30
100,200,300https://gist.github.com
1,2,3
10,20,30
100,200,300
このように
$ cat sample.csv | csvremap sample.js
a,b,c
1,2,3
foo,hoge,30
bar,200,300
1,2,3
foo,hoge,30
bar,200,300
1,2,3
foo,hoge,30
bar,200,300
1,2,3
foo,hoge,30
bar,200,300
1,2,3
foo,hoge,30
bar,200,300
となる、というだけのコマンド。jsonってのがなんともアレだけど、動けばいいんです動けば。わりと活躍するかもしれん。

sample.jsはこんな感じ
$ cat sample.js
 {"a":{
         "10":"foo"
       , "100":"bar"
      }
, "b":{
        "20":"hoge"
      }
}
cut,sort,uniqなんかのコマンドと相性いいかも(そらそうだろうな)。
$ cat sample.csv | csvremap sample.js | tail -n +2 | sort -nr | uniq
1,2,3
foo,hoge,30
bar,200,300
で、こうやってmap系のコマンド作ると絶対reduce系のコマンド欲しくなるな(笑)

ソース

こちら。gauche。
#!/usr/local/bin/gosh
(use srfi-1)
(use text.csv)
(use file.util)
(use rfc.json)
(use util.list)
(use gauche.parseopt)
(define (usage)
(print "Usage: csvremap [opt ...] <mapping-table-json> <csv-file>")
(print " h|help => print this usage")
(print " d|delimiter => delimiter (default ,)")
(print " t|tsv")
(print " json spec => '{csv-column-name:{csv-column-value:substitution, ...}, ...}'")
(exit 1))
(define (write-csv csv port :optional (delim #\,))
(let1 writer (make-csv-writer delim)
(for-each (pa$ writer port) csv)))
(define (csv->list file :optional (delim #\,))
(port->list (make-csv-reader delim)
(if file
(open-input-file file)
(current-input-port))))
(define (file->json file)
(parse-json-string
(if file
(file->string file)
(port->string (current-input-port)))))
(define (map-csv-columns csv table)
(if (or (null? csv)
(null? table))
csv
(let ((header (car csv))
(csv (cdr csv)))
(cons header
(map (^[csv-row]
(map (^[name val]
(let1 table-row (assoc-ref table name)
(let1 new-val (and table-row (assoc-ref table-row val))
(or new-val val))))
header csv-row))
csv)))))
(define (main args)
(let-args (cdr args)
((help "h|help" => usage)
(delim "d|delimiter=s" ",")
(tsv "t|tsv")
(else (opt . _)
(print "Unknown option : " opt)
(usage))
. rest)
(when (null? rest)
(print "wrong number of arguments. required 1, got 0.")
(usage))
(let* ((delim (if tsv #\tab (~ delim 0)))
(table (file->json (and (not (null? rest))(car rest))))
(csv (csv->list (and (not (null? (cdr rest)))
(cadr rest)) delim)))
(write-csv (map-csv-columns csv table)
(current-output-port)
delim))))
view raw csvremap.scm hosted with ❤ by GitHub
いやー、やっぱS式っていいですね。人にやさしい脳にやさしい。

追記

jsonを標準入力から受け取るようにしてたけど、引数の順序を変えてCSVを標準入力から受け取るようにした。どう考えてもCSVの方が加工されてわたってくることが多いもんな。使ってて不便だった。

プログラミングGauche

2012/03/07

2012/03/07

KPF(熊本プログラミングフリークス)の勉強会を開催します

ブログで告知するの久しぶり。 プログラミングが好きな人は参加すると楽しいと思います。これからプログラミングを始めたい人、LTしてみたい人、ヘンタイ(いい意味)、仕事のプログラミングで悩んでる人、面白いもの作ったので発表したい人、変な人(面白い人・すごい人)に会いたい人など、これまでいろんな人に参加して頂いてます。

2012/03/31(土)の13時からです。会場は東海大学熊本キャンパスにお世話になります。ひやかし、のぞき見、歓迎します。

LT応募多すぎて入り切らないかもしれません(笑)

日記(読み物)

読み物といってもプログラミング関係 じゃない方。 翻訳のところに面白いのがたくさんある。 しばらく前にいくつか読んでみて面白かった。並んでる作品はたぶん有名所なんだろうけどよう知らん。こういうのがあるってのを忘れてたので思い出したついでにメモ。また暇なときに読もうかなと。

このへんは読んだ記憶がある。
きっかけはなんだったかなー・・・と思いだしてみたらこちらの記事だった。

C#でREPL(Windows)

string.Formatのフォーマット指定子全部覚えてます?Enumerable.Range(0, 10)の結果が0から10までなのか0から9までなのか覚えてます?僕は覚えてないのでググるか、試しに書いてみます。consoleプロジェクト作って試したり、今書いてるソースに応急的に書き足して試したりするのって面倒じゃないですか?みんなどうやってんの?ConsoleApplication58.csとか作ってやってんの?REPL欲しくないですか? なのでvpsにmono入れてsshでcsharpを使って試してました。が、monoのwindows向けのバイナリ入れればいい話でした。アホ過ぎた。csharp.bat叩けばcmdでreplが立ち上がるのでお手軽。

Effective C# 4.0

2012/03/05

2012/03/05

日記(業務アプリ開発)

久しぶりに既存業務アプリ保守のお仕事。C#。業務アプリ開発はほとんどSQLとリファクタリングがメインだなと。いい機会なので勉強しますか。幸いまーちんふぁうらー先生の本を積んでるので消化しましょう。あ、コードコンプリート巻を再読もいいかもしれん。しかし久しぶりを差し引いてもSQL難しい。C#も。S式はあんなに親切なのに・・・。というか脳スペックが悪いのでS式のシンプルさに慣れると戻れんのかもしれん。

リファクタリング―プログラムの体質改善テクニック (Object Technology Series)

日記(2012年)

最近、読書もプログラミングもご無沙汰。去年の秋頃から私的な面で忙しいのが理由の一つ。去年の9月から今年の5月末まで週末はほとんど予定で埋まってる。リア充。2月に転職したのも理由の一つ。いくら楽しい職場と言っても新しい環境は何かと疲れる。 ということで今年は6月くらいまで、空いた時間は積極的にだらだらごろごろする。もしくは筋トレと水泳でリフレッシュだな。筋トレは4ヶ月超えてから効果出てきて(というより腹筋のやり方変えてから?)腹筋割れてきた。けど、徐々に筋肉痛にならなくなるとともに効果が落ちてきた。もっと負荷のかかるトレーニングに変えた方がいいのかな。