nbld

寒くなるこの時期、冬野菜が美味しい。筆頭は白菜。

地元の野菜コーナーで、大玉が200円、小玉が108円と庶民的な値段。大玉はお一人様2個って制限付き。中には、一家総出で買いに来る家族が居る。韓流ブームでキムチでも漬けるんでしょうかね? 柿の皮やらリンゴの皮やら、和唐辛子を入れて、日韓協力だったりして。

生憎、オイラーは鼻の曲がるようなキムチは大嫌い。よって、大量に買う事は無い。鍋用に週に1個も有れば十分。

鍋と言えば、貧乏鍋が定番。1個100円のさばの缶詰があれば、材費200円で腹一杯になる。が、この所安いさば缶が手に入らなくなっている。

どのスーパーへ行っても、さば缶のコーナーには、某TVの番組の影響でさば缶の品不足が続いています、供給が不安定な為ご了承下さいって言う張り紙が目立つ。

そのくせ、1個400円とかの高級さば缶は、誰も買わないせいか大量に陳列されている。 これって、さばマフィアの暗躍かな?

アベーちゃんよ、ちゃんとご指導宜しく。

CentOS Desktop login fail

いつの間にかCentOS7のDeskTopへ入れない状況になってた。sshでCUI端末では使えるのだけどね。管理者の癖を学習して、GUIはOS自らloginをしない設定にしちゃったの?

ええ、ちゃんとgdmの画面は出てるので、パスワードを入力すると、暫く待たされてGnomeが動き出すかと思いきや、元のgdmな画面に戻ってしまう。

但し、gdmに戻る時の画面のちらつき具合が、logout時のちらつき具合に似てる事に気が付いた。ログインした瞬間に追い出されてしまった感じがする。

ネットを検索するも、こんな症例にはお目にかかれなかった。ひょっとして症例の訴え方が不適切だったかも?どうやったら修復出来るのだろう? 色々なプロセスが動いているし、ログを見ても、手がかりになりそうなものは書かれていなかった。だから、GUIって嫌いなんだよなー。

で、一つやる事を思い付いた。Home下にあるGUIに関係してそうなフィアルやらDirを片っ端から消してみる事。やったけどだめだーーー。OSごと消してしまいたいぞ。

まてまて、新しいDeskTop環境を作ってみるって言う手があるな。どうやって? そんなの簡単。新しいユーザーを作れば良い。

adduser ham って叩いたよ。そしたらpasswordって聞いてきたんで返答。これで、/home/hamが出来上がって、/etc/passwdを見る限り、新規ユーザーが出来た風。

早速GUIでログイン。が、何度トライしてもアクセスが拒否された。何でかなーーー。ええい、こうなったら得意のCUI戦法だな。

ssh ham@localhost したよ。パスワードを入れると、Permission denied って言ってきた。

えっ、新規ユーザーを登録した時に、ちゃんとパスワードを登録したはず。念のために、sudo cat /etc/shadow してみた。そしたら、本来パスワードが置かれるべき所が、-Uなんていう不適文字になってたぞ。

しょうがないので、sudo passwd ham して、再設定。これでCUI/GUI共に無事にログイン出来た。adduserで登録されるのはロックされてるのね。manを見ればちゃんと書いて有るんだろうけど、見る気Nothing。

と言う事で、新規ユーザーでは無事にDeskTop login出来る事が確認出来た。後は、既存のユーザーのdeskTopの何処かが壊れてるって事になるけど、どこをどうつつけば良いものか? 皆目見当が付きませんよ。

ham      pts/0        xxx.xxx.xxx.xxx    Thu Nov 22 14:09   still logged in
sakae    :0           :0                 Thu Nov 22 14:08    gone - no logout

ひょっとして、この見慣れないlastの出力が、手がかりになるかなあ?

まあいいや、先に進もう。んで、取り合えずでもDeskTopを動かしたかったか? それは次節に続く。

method chain

昔書いた血圧グラフソフト。golangを使って入力。haskellを使ってgnuplotに渡すデータの抽出。データが揃った所でバッチでgnuplotスクリプトの実行って流れになってる。haskellでやってた所はnewlispで書いていたりもした。まるで、言語の入れ喰い競争みたいだ。

今後の事も有るんで、なるべくgolang一本にしておきたい。(とはいえ、gnuplotのスクリプトはそのまま使う。気の迷いで、githubから、グラフパッケージなんかを拾ってくるなよ)

かの昔には色々な(フィルター)機能を付けていたけど、今はさっぱり使っていない。だったら、機能を整理しよう。直近のデータについてグラフ化出来れば十分。

多機能化のために、たとえば

func tl(n int, ds AAy) AAy {
        if len(ds) < n {
                fmt.Println("Warnning: Req size is to big, apply possible size.")
                return ds
        }
        return ds[len(ds)-n:]
}

こんな風に、加工すべきデータを関数の引数として受け取っていた。パイプを多段に渡って組み立てる方式。

でも、実現すべき機能が固定なら、メソッドにしちゃえ。そうすれば、 オブジェクト指向っぽく出来るぞ。ああ、オブジェクト指向ってのは、「自分自身の振る舞いを知っているのがオブジェクト」って事ね。

上記の例なら、渡ってくるデータdsの尻尾の部分n個を返すってやつ。(ds AAy)ってのを前にくくり出せば、オブジェクト指向っぽくなる。

そうすれば、rubyみたいにメソッドチェーンが簡単に実現出来るぞ。んな訳で、機能を絞ったものに改造した。(ソースは、後掲)

今回の理屈を要領よくまとめた記事が出てた。

Golangでメソッドチェーン

ちゃぶ台返し

喜んでいたら、以下のような記事を目にした。ちゃぶ台返しである。

Go における FunctionalOptionPattern と MethodChaining について考える

Go言語のFunctional Option Pattern

Functional Option Pattern

Go 言語における「オブジェクト」

nbld

// blood PNG(pdf) by gnuplot
// Using gnuplot script toPNG.plt topdf.plt
package main

import (
        "encoding/csv"
        "flag"
        "fmt"
        "io/ioutil"
        "os"
        "os/exec"
        "strconv"
        "strings"
)

const dbfile = "current.csv"

type AAy [][4]int // main data type
var bld AAy       // blood data

const ( // rows index name 0 .. 3
        ymdh = iota
        hi
        low
        pls
)

func failOnError(err error) {
        if err != nil {
                panic(err)
        }
}

func mycsv(csvfile string) {
        var rows [4]int
        fd, err := os.Open(csvfile)
        failOnError(err)
        defer fd.Close()
        reader := csv.NewReader(fd)
        all, err := reader.ReadAll()
        failOnError(err)
        for _, el := range all {
                for i, v := range el {
                        rows[i], err = strconv.Atoi(v)
                        failOnError(err)
                }
                bld = append(bld, rows)
        }
}

func savecsv(csf string) {
        buf, _ := ioutil.ReadFile(csf)
        _ = ioutil.WriteFile("Backup.csv", buf, 0666)
        fd, err := os.OpenFile(csf, os.O_CREATE|os.O_WRONLY, 0666)
        failOnError(err)
        err = fd.Truncate(0)
        failOnError(err)
        defer func() {
                fd.Close()
        }()
        w := csv.NewWriter(fd)
        for _, el := range bld {
                rows := make([]string, 0)
                for _, v := range el {
                        rows = append(rows, strconv.Itoa(v))
                }
                w.Write(rows)
        }
        w.Flush()
}

func ire(ym int) {
        var a, b, c, d string
        var rs [4]int
        for {
                a, b, c, d = "", "", "", ""
                fmt.Printf("%d> ", ym)
                fmt.Scanln(&a, &b, &c, &d)
                if a == "fin" {
                        break
                }
                rs[ymdh], _ = strconv.Atoi(a)
                rs[hi], _ = strconv.Atoi(b)
                rs[low], _ = strconv.Atoi(c)
                rs[pls], _ = strconv.Atoi(d)
                if rs[ymdh] > 3123 || rs[hi] < 80 ||
                        rs[low] < 40 || rs[pls] < 40 {
                        fmt.Println("Bad data")
                        continue
                }
                rs[ymdh] += (ym * 10000)
                if rs[ymdh] <= bld[len(bld)-1][ymdh] {
                        fmt.Println("Bad seq.")
                        continue
                }
                bld = append(bld, rs)
        }
        savecsv(dbfile)
}

func (ds AAy) pp(fn string) {
        xf,_ := os.Create(fn)
        defer xf.Close()
        for _, el := range ds {
                fmt.Fprintf(xf, "%d %d %d\n", el[ymdh], el[hi], el[low])
        }
}

func (ds AAy) tl(n int) AAy {
        if len(ds) < n {
                fmt.Println("Warnning: Req size is to big, apply possible size.")
                return ds
        }
        return ds[len(ds)-n:]
}

func (ds AAy) am() AAy {
        var rs AAy
        for _, el := range ds {
                if el[ymdh]%100 < 12 {
                        rs = append(rs, el)
                }
        }
        return rs
}

func (ds AAy) pm() AAy {
        var rs AAy
        for _, el := range ds {
                if el[ymdh]%100 >= 12 {
                        rs = append(rs, el)
                }
        }
        return rs
}

func main() {
        var vire *int = flag.Int("ire", 1811, "Input YYMM's data")
        flag.Parse()
        args := ""
        flag.Visit(func(f *flag.Flag) {
                args += fmt.Sprintf(":%s: ", f.Name)
        })

        mycsv(dbfile)       // output is bld
        if strings.Index(args, ":ire:") >= 0 {
                ire(*vire)
        }

        sz := 70
        bld.am().tl(sz).pp("am.dat")
        bld.pm().tl(sz).pp("pm.dat")

        exec.Command("gnuplot", "toPNG.plt").Run()
        exec.Command("gnuplot", "topdf.plt").Run()
}

toPNG.plt topdf.plt

gnuplot用スクリプト

debian:blood$ cat toPNG.plt
# blood graph
set terminal pngcairo mono font "Ryumin-Light-90pv-RKSJ-H,10" size 21cm,28cm
set output "./zzz.png"

stats "am.dat" using 2:3
amh = sprintf(" 最高血圧(平均=%.1f 標準偏差=%.1f)", STATS_mean_x, STATS_stddev_x)
aml = sprintf(" 最低血圧(平均=%.1f 標準偏差=%.1f)", STATS_mean_y, STATS_stddev_y)
stats "pm.dat" using 2:3
pmh = sprintf(" 最高血圧(平均=%.1f 標準偏差=%.1f)", STATS_mean_x, STATS_stddev_x)
pml = sprintf(" 最低血圧(平均=%.1f 標準偏差=%.1f)", STATS_mean_y, STATS_stddev_y)

set grid
set yrange [50:160]
set ytics 10
unset key                   # no label on right top
set xdata time
set timefmt "%y%m%d%H"
set format x "%m/%d"        # m/d/y -> m/d

set multiplot layout 2,1

set title '起床時: ' . amh . aml
plot "am.dat" using 1:2 with lines, "am.dat" using 1:3 with lines

set title '就寝時: ' . pmh . pml
plot "pm.dat" using 1:2 with lines, "pm.dat" using 1:3 with lines

unset multiplot
set terminal dumb
debian:blood$ diff -u toPNG.plt topdf.plt
--- toPNG.plt   2018-11-25 06:11:49.384280556 +0900
+++ topdf.plt   2018-11-25 06:12:29.440119619 +0900
@@ -1,13 +1,13 @@
 # blood graph
-set terminal pngcairo mono font "Ryumin-Light-90pv-RKSJ-H,10" size 21cm,28cm
-set output "./zzz.png"
+set terminal pdfcairo mono font ",20" size 21cm, 29cm
+set output "./zzz.pdf"

good code by golint

golangのコードを書く時、お勧めのスタイルがあるとか。それを外れた場合に文句を言ってくれる、golintって言うツールが有るそうな。そういうのは、早い段階で導入して、独りよがりにならないようにしよう。

下記は、その実行例

sakae@usvr:~/go/src/blood$ go get github.com/golang/lint/golint
sakae@usvr:~/go/src/blood$ ~/go/bin/golint nbld.go
nbld.go:18:6: exported type AAy should have comment or be unexported
nbld.go:178:11: should omit type *int from declaration of var vire; it will be inferred from the right-hand side

上に載せたコードを点検して貰った所、2点注意された。こうして金太郎飴みたいになっていくんだな。まるでPythonみたいにね。

;; To install golint, add the following lines to your .emacs file:
;;   (add-to-list 'load-path "PATH CONTAINING golint.el" t)
;;    or golint.el into lisp dir
;;   (setq load-path (append '("~/.emacs.d/lisp" ) load-path))
;;   (require 'golint)
;;
;; After this, type M-x golint on Go source code.
;;
;; Usage:
;;   C-x `
;;     Jump directly to the line in your code which caused the first message.

おまけが付いていた。コンパイルエラーの修正方法と同じ扱いになってて便利。vim屋さん用のプラグインも同梱されてたな。(使わない、オイラーには使えないけど)

auto-complete

前回入れた、golang用のGUI版開発環境、liteideにも補完機能が付いていた。 今emacs上で使ってるcompany-goと挙動が違う。はてな?と思ったら、emacs上で補完を提供するのには、2つの流派が有るのね。知らんかった。

もう一方の補完は、auto-complete派。ネットを調べたら、 「Emacsのトラノマキ」連載第09回「auto-completeを使おう」(松山智大)なんてのが見つかった。日本製を使ってみるべし。go-autocompleteも合わせて入れる。下記がその設定。

(with-eval-after-load 'go-mode
  ;; auto-complete
  (require 'go-autocomplete)
  (require 'auto-complete-config)
  (ac-config-default)
  (setq ac-use-menu-map t)
  (set-face-background 'ac-selection-face "blue")

  ;; godef key bindings
  (define-key go-mode-map (kbd "M-.") 'godef-jump)
  (define-key go-mode-map (kbd "M-,") 'pop-tag-mark))

これで、挙動がliteideと同一になった。めでたしめでたし。