#!/bin/awk -f BEGIN{ 〜 } /hoge/{ 〜 }
#!/usr/bin/env ruby
#!/bin/sh exec ruby -S -nax $0 "$@" #! ruby
#!/usr/bin/ruby -na
項目 | awk | ruby |
フィールドセパレータ | FS | $; |
出力フィールドセパレータ | OFS | $, |
レコードセパレータ | RS | $/ |
出力レコードセパレータ | ORS | $\ |
カレント行番号 | NR | $. |
分割されたフィールド | $1, $2,... | $F[0],$F[1],... |
パターンマッチ | $1 ~ /正規表現/ | if $F[0] =~ /正規表現/ |
BEGINブロック内変数のスコープ | グローバル |
1.8まではBEGINブロック内、
2.0以降はグローバル
|
next文のふるまい |
次の入力行を読み込み、
スクリプトの最初から実行
|
ループを1個抜けるだけ
|
#!/usr/bin/env ruby
#
# stream.rb -- GDSII Data Parser
# "Stream Format" is output format of GDSII data.
#
# Todo:
# - do like ruby.
# - probably bug about upper/lower nibble
#
# Reference:
# GDSII Format: http://www7b.biglobe.ne.jp/~garaku/HSGDS/GDSII_p01.html
#
# History:
# 0.2 change method to class of each record, add Colrow
# 0.1 implement some records will be used usually
#
# Author:
# M.Hori
####################
# Constant
####################
# Variables
MY_VERSION = '0.2'
USAGE_MSG = "Usage: " << $PROGRAM_NAME << " [options] GDSII_File"
####################
# Module
####################
module Util
# Data Converter
def int2(input)
input.unpack("n")[0]
end
def int4(input)
input.reverse.unpack("l*")[0]
end
def real4(input)
end
def real8(input)
# S=sign, E=index, M=mantissa
# SEEEEEEE MMMMMMMM MMMMMMMM ...(total 7 byte)... MMMMMMMM
# real8 = (-1)^sign * (0.mantissa)_2 * 16^(index-64)
sign = (input & 0x8000000000000000)>>63
index = (input & 0x7F00000000000000)>>56
mantissa = (input & 0x00FFFFFFFFFFFFFF)/(2.0**56)
((-1)**sign)*(mantissa*16**(index-64))
end
def ascii(input)
input.unpack("A*")
end
# long print
def lpr(*var)
printf(*var) if $long
end
module_function :int2, :int4, :real4, :real8, :ascii, :lpr
end
####################
# クラス
####################
class Record
# 2byte ==> unpack("n") # convert to big endian (byte order)
# 1byte ==> unpack("c")
# +----------------------+
# | size | 2 byte
# +-----------+----------+ --+
# |record_type|_data_type| 1 byte + 1 byte |
# +-----------+----------+ +-- others
# | data | (size-4) byte |
# | : | |
# +----------------------+ --+
# readable instance variables
attr_reader :record_type, :_data_type, :data
# Array
@@record_type_array = [
"HEADER", "BGNLIB", "LIBNAME", "UNITS", "ENDLIB",
"BGNSTR", "STRNAME", "ENDSTR", "BOUNDARY", "PATH",
"SREF", "AREF", "TEXT", "LAYER", "DATATYPE",
"WIDTH", "XY", "ENDEL", "SNAME", "COLROW",
"TEXTNODE", "NODE", "TEXTTYPE", "PRESENTATION","SPACING",
"STRING", "STRANS", "MAG", "ANGLE", "UNITEGER",
"USTRING", "REFLIBS", "FONTS", "PATHTYPE", "GENERATIONS",
"ATTRTABLE","STYPTABLE","STRTYPE", "ELFLAGS", "ELKEY",
"LINKTYPE", "LINKKEYS", "NODETYPE", "PROPATTR", "PROPVALUE",
"BOX", "BOXTYPE", "PLEX", "BGNEXTN", "ENDEXTN",
"TAPENUM", "TAPECODE", "STRCLASS", "RESERVED", "FORMAT",
"MASK", "ENDMASKS", "LIBDIRSIZE","SRFNAME", "LIBSECUR",
]
@@data_type_array = [
"NO_DATA", "BIT_ARRAY", "INT_2", "INT_4", "REAL_4",
"REAL_8", "STRING",
]
# Initializer
def initialize(size, others)
@size = size
@record_type, @_data_type = others[0,2].unpack("cc")
@data = others[2,size-4] # size=2, record=1, data=1, total=4byte
end
# to_s
def to_s
return @@record_type_array[@record_type],
@@data_type_array[@_data_type]
end
# no_operation
def no_operation
Util.lpr(" --------------------")
end
# Parser
def parse_data
case record_type
when 0 # HEADER
Util.lpr(" GDSII_Version=%s", Header.new(@data).gds_version)
when 1 # BGNLIB
_m, _a = Bgnlib.new(@data).date
Util.lpr(" Mod=%s Acc=%s", _m, _a)
when 2 # LIBNAME
Util.lpr(" LibraryName=%s", Libname.new(@data).name)
when 3 # UNITS
#units()
_uu, _um = Units.new(@data).units
Util.lpr(" dbUnitByUserUnit=%.1e dbUnitByMeter=%.1e", _uu, _um)
when 4 # ENDLIB
no_operation()
when 5 # BGNSTR
$log["structures"] += 1
_m, _a = Bgnstr.new(@data).date
Util.lpr(" Mod=%s Acc=%s", _m, _a)
when 6 # STRNAME
Util.lpr(" StructureName=%s", Strname.new(@data).name)
when 7 # ENDSTR
no_operation()
when 8 # BOUNDARY
$log["boundaries"] += 1
no_operation()
when 9 # PATH
$log["paths"] += 1
no_operation()
when 10 # SREF
$log["srefs"] += 1
no_operation()
when 11 # AREF
$log["arefs"] += 1
no_operation()
when 12 # TEXT
$log["texts"] += 1
no_operation()
when 13 # LAYER
Util.lpr(" LayerNumber=%d", Layer.new(@data).number)
when 14 # DATATYPE
Util.lpr(" DataType=%d", Datatype.new(@data).number)
when 15 # WIDTH
Util.lpr(" Width=%.1f", Width.new(@data).width)
when 16 # XY
Util.lpr(" %s", Xy.new(@data).xy_list)
when 17 # ENDEL
no_operation()
when 18 # SNAME
Util.lpr(" StructureName=%s", Sname.new(@data).name)
when 19 # COLROW
_col, _row = Colrow.new(@data).colrow
Util.lpr(" Col=%d, Row=%d", _col, _row)
when 22 # TEXTTYPE
Util.lpr(" Text_Type=%d", Texttype.new(@data).number)
when 23 # PRESENTATION
_obj = Presentation.new(@data)
Util.lpr(" Font#=%d Origin=%s", _obj.font, _obj.origin)
when 25 # STRING
Util.lpr(" String=%s", Strings.new(@data).name)
when 26 # STRANS
_obj = Strans.new(@data)
Util.lpr(" Mirror=%s Mag=%d Angle=%d",
_obj.mirror_flag, _obj.mag_flag, _obj.angle_flag)
when 27 # MAG
Util.lpr(" Mag=%.1e", Mag.new(@data).value)
when 28 # ANGLE
Util.lpr(" Angle=%.1f", Angle.new(@data).value)
when 33 # PATHTYPE
Util.lpr(" PathType=%d", Pathtype.new(@data).number)
when 48 # BGNEXTN
Util.lpr(" ProjectionSize=%.1f", Bgnextn.new(@data).width)
when 49 # ENDEXTN
Util.lpr(" ProjectionSize=%.1f", Endextn.new(@data).width)
end
end
end
# HEADER
class Header
def initialize(data)
@data = data
end
def gds_version
case @data.unpack("n")[0]
when 0 then @ver = "v3.0"
when 3 then @ver = "v3.0"
when 4 then @ver = "v4.0"
when 5 then @ver = "v5.0"
when 600 then @ver = "v6.0"
else @ver = "unknown"
end
@ver
end
end
# BGNLIB
class Bgnlib
def initialize(data)
@data = data
end
def date
my,mm,md,mh,mn,ms,ay,am,ad,ah,an,as = @data.unpack("n12")
@last_modify =
"%4d/%02d/%02d,%02d:%02d:%02d"%([1900+my,mm,md,mh,mn,ms])
@last_access =
"%4d/%02d/%02d,%02d:%02d:%02d"%([1900+ay,am,ad,ah,an,as])
return @last_modify, @last_access
end
end
# LIBNAME
class Libname
def initialize(data)
@data = data
end
def name
Util.ascii(@data)
end
end
# UNITS
class Units
def initialize(data)
@data = data
end
def units
# unpack("H16") H: Hex(upper nibble first)
@unit_by_uu = Util.real8(@data[0, 8].unpack("H16").join.hex)
@unit_by_meter = Util.real8(@data[8, 8].unpack("H16").join.hex)
return @unit_by_uu, @unit_by_meter
end
end
# BGNSTR
class Bgnstr < Bgnlib
end
# STRNAME
class Strname < Libname
end
# LAYER
class Layer
def initialize(data)
@data = data
end
def number
Util.int2(@data)
end
end
# DATATYPE
class Datatype < Layer
end
# WIDTH
class Width
def initialize(data)
@data = data
end
def width
Util.int4(@data)
end
end
# XY
class Xy
def initialize(data)
@data = data
end
def xy_list
# unpack("l*") : long(32bit signed int)
# reverse : convert endian
# map(&:to_i) : convert array to int
xys = @data.reverse.unpack("l*").map(&:to_i)
@xy_list = ""
xys.each_slice(2) do |x, y|
@xy_list <<= "(" << x.to_s << "," << y.to_s << ")"
# '<<' beter more than '+' for cat strings
end
@xy_list
end
end
# SNAME
class Sname < Libname
end
# COLROW
class Colrow
def initialize(data)
@data = data
end
def colrow
@data.unpack("n2")
end
end
# TEXTTYPE
class Texttype < Layer
end
# PRESENTATION
class Presentation
def initialize(data)
@data = data
end
def font
@data.unpack("c2")[0].to_i
end
def origin
text_origin_array = [
"upperLeft", "upperCenter", "upperRight", nil,
"centerLeft", "centerCenter", "centerRight", nil,
"lowerLeft", "lowerCenter", "lowerRight", nil
]
text_origin_array[@data.unpack("c2")[1].to_i]
end
end
# STRING
class Strings < Libname
end
# STRANS
class Strans
def initialize(data)
@data = data
end
def mirror_flag
(@data.unpack("c2")[0] & 0x80)>>7
end
def mag_flag
(@data.unpack("c2")[1] & 0x04)>>2
end
def angle_flag
(@data.unpack("c2")[1] & 0x02)>>1
end
end
# MAG
class Mag
def initialize(data)
@data = data
end
def value
Util.real8(@data[0, 8].unpack("H16").join.hex)
end
end
# ANGLE
class Angle < Mag
end
# PATHTYPE
class Pathtype < Layer
# 0=no projection,1=half round projection,
# 2=half width projection, 4=width is BGNEXTN,ENDEXTN
end
# BGNEXTN
class Bgnextn < Width
end
# ENDEXTN
class Endextn < Width
end
####################
# Main
####################
# Variables
$log = {
"records" => 0,
"structures" => 0,
"boundaries" => 0,
"paths" => 0,
"srefs" => 0,
"arefs" => 0,
"texts" => 0,
}
# Method
def usage()
STDERR.printf("%s\n", USAGE_MSG)
end
def cprint(*var)
console = open('/dev/tty', "w") do |con|
con.printf(*var)
end
end
# Parse Options
require 'optparse'
OptionParser.new do |opt|
opt.banner = USAGE_MSG
opt.version = MY_VERSION
opt.on('-l', 'print long format information') { |v| $long = v }
begin
opt.parse!(ARGV)
rescue OptionParser::InvalidOption => e
puts e.message
usage
exit
end
end
# Open File then Read and Parse
INPUT_FILE = ARGV[0]
if !INPUT_FILE then usage; exit end
begin
# not neccesary file.close
File.open(INPUT_FILE, "rb") do |file|
record = {}
c = 0
$bgnstr = 0; $boundary = 0; $path =0;
$sref = 0; $aref = 0; $text = 0
# Display Title
Util.lpr("%4s %3s %-12s %-9s %s\n",
"#", "byte", "RecType", "DataType",
"Contents (XY,WIDTH is dbUnit)")
# Read File
while !file.eof
# Get Record Size
size = file.read(2).unpack("n")[0] # to big endian
if size > 0
data = file.read(size-2) # get remain record
record[c] = Record.new(size, data)
_record_type, _data_type = record[c].to_s
Util.lpr("%4d: %3d %-12s %-9s",
c, size, _record_type, _data_type)
record[c].parse_data()
Util.lpr("\n")
else
break
end
c += 1
cprint("\r%d records loaded.", c) if !$long
end
cprint("%s: %d records, %d structures, %d boundaries, %d paths, %d srefs, %d arefs, %d texts\n",
INPUT_FILE, c, $log["structures"], $log["boundaries"], $log["paths"], $log["srefs"], $log["arefs"], $log["texts"])
end
rescue Errno::ENOENT
STDERR.printf("\n%s not found.\n", INPUT_FILE)
rescue Errno::EACCES
STDERR.printf("\ncannot open %s.\n", INPUT_FILE)
rescue => e
STDERR.printf("\nexception: class=#{e.class}, msg=#{e.message}\n")
end
#EOF
定義は以下。
Z = 50 + 10*(x - m)/σ
ここで、Zが偏差値、xが自分の点数、mが平均点、σが標準偏差です。
つまりは、平均点との差をとることで平均を0に、σで割って10掛けることで標準偏差を10に正規化し、50を足すことで平均を50にしています。ここで、なぜ標準偏差が10で平均が50かっていうと、特に数学的な意味はなく、単にそうした、というもののようです。
ただの正規分布の話になってしまいますが、偏差値60(平均+1σ)なら上位16%に、70(平均+2σ)なら上位3%にいる、ということになります。目安の数字として覚えておくといいですね。
ただし、一般の試験において、平均点は発表されることが多いので知ることができますが、標準偏差は発表されませんし、全員の点数がわからないと計算できないので、上記の定義式では偏差値を算出するのは困難です。
そこで、簡易的な式があります。
Z' = 50 + (x - m)/2
ZとZ'を比べてみると、σ=20と見なしていることがわかります。これは、一般的にテストはσ=20を目指して設計されることから来ているようです。
これなら平均点がわかれば計算できます。100点満点で、平均点が50点、自分が60点なら、偏差値は55ということです。
]]>遺伝的アルゴリズムは、基本的なものは実装も簡単なので、素人でもその強力さが実感できて面白いです。こちらの「エンジニアが正しく「I love you」と伝えるための遺伝的アルゴリズム(殺伐) 」というページを拝見して、興味が再燃したので、おなじ課題をCで組んでみました。ランダムな文字列から出発して、世代交代を繰り返し、「I love you」にたどり着けるか?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#define TARGET "I love you"
#define MUTATE_CHANCE 0.5
#define GENE_NUM 20
#define NUM_OF_MUTATE 1
#define ASCII_MIN 32
#define ASCII_MAX 127
typedef struct {
char *gstr;
int gval;
}GENE;
GENE gene[GENE_NUM];
int gstr_len;
int debug = 0;
void init_gene( void );
int eval( char * );
void gene_sort( void );
int comp( const void *, const void * );
void cross( void );
void mutate( float );
void pr( void );
void pr_usage( void );
// compare for qsort
int comp( const void *c1, const void *c2 ) {
GENE t1 = *(GENE *)c1;
GENE t2 = *(GENE *)c2;
return t1.gval - t2.gval;
}
// init gene
void init_gene() {
int i, j;
// allocate memory
gstr_len = strlen( TARGET );
for ( i = 0; i < GENE_NUM; i++ ) {
gene[i].gstr = (char *)malloc( sizeof(char)*(gstr_len+1) );
if ( gene[i].gstr == NULL ) {
printf( "memory allocation faild.\n" );
exit( EXIT_FAILURE );
}
}
// generate string & sort
srand( (unsigned)time( NULL ) );
for ( i = 0; i < GENE_NUM; i++ ) {
for ( j = 0; j < gstr_len; j++ ) {
sprintf( &gene[i].gstr[j], "%c", rand()%(ASCII_MAX-ASCII_MIN)+ASCII_MIN );
}
gene[i].gstr[j+1] = '\0';
gene[i].gval = eval( gene[i].gstr );
}
qsort( gene, sizeof(gene)/sizeof(gene[0]), sizeof(gene[0]), comp );
}
// mutation
void mutate( float chance ) {
int i, j, k;
int pm;
for ( i = 0; i < GENE_NUM; i++ ) {
if ( chance*10 < rand()%10 ) {
j = rand()%gstr_len;
pm = rand()%2 == 0 ? 1 : -1;
if ( gene[i].gstr[j] == ASCII_MIN) pm = 1;
if ( gene[i].gstr[j] == ASCII_MAX) pm = -1;
gene[i].gstr[j] += pm;
}
}
}
// evaluation
int eval( char *str ) {
int i, v;
int r = 0;
char target[] = TARGET;
for ( i = 0; i < gstr_len; i++ ) {
v = strncmp( target+i, str+i, 1 );
r += v*v;
}
return( r );
}
// select & cross
void cross() {
int i, cp;
cp = rand()%gstr_len;
for ( i = 0; i < gstr_len; i++ ) {
if ( i < cp ) {
gene[GENE_NUM-2].gstr[i] = gene[0].gstr[i];
gene[GENE_NUM-1].gstr[i] = gene[1].gstr[i];
} else {
gene[GENE_NUM-2].gstr[i] = gene[1].gstr[i];
gene[GENE_NUM-1].gstr[i] = gene[0].gstr[i];
}
}
}
void pr() {
int i;
for ( i = 0; i < GENE_NUM; i++ ) {
printf( " >%2d %10s %d\n", i, gene[i].gstr, gene[i].gval );
}
}
// usage
void pr_usage() {
printf( "usage: ga [-dh]\n" );
}
// main
int main( int argc, char **argv ) {
int i, j;
int generation = 0;
int opt;
// optrion
while ( ( opt = getopt( argc, argv, "dh" ) ) != -1 ) {
switch ( opt ) {
case 'd': debug = 1; break;
case '?':
case 'h':
default: pr_usage(); exit( EXIT_SUCCESS );
}
}
// init
init_gene();
if ( debug ) pr();
// loop
while ( gene[0].gval > 0 ) {
// select & cross
cross();
// mutation
mutate( MUTATE_CHANCE );
// evaluation
for ( i = 0; i < GENE_NUM; i++ ) {
gene[i].gval = eval( gene[i].gstr );
}
// sort
qsort( gene, sizeof(gene)/sizeof(gene[0]), sizeof(gene[0]), comp );
printf( "%4d %5d %10s\n", generation, gene[0].gval, gene[0].gstr );
if ( debug ) pr();
generation++;
}
}
実行結果がこちら。左端の数字は世代です。2列目は、「I love you」からどれだけ遠いか、3列目がその文字列。2, 3列目はその世代で最も優秀な個体のものが示されています。
「I love you」にたどり着くと実行が停止します。数百世代を経て、ようやくたどり着きました。でもCygwinでも実行時間はあっという間です。
何世代くらいでたどり着くのか、100回繰り返してみました。横軸が世代数で、縦軸が「I love you」からどれくらい遠いか、です。0でたどり着いたことになるのですが、0近辺を拡大するために logスケールにしてみました。なので1が最小値です。1にたどり着く世代数には、結構、幅があります。
今回の例では、1世代の個体数は20で、ベストな2個体で交叉してワースト2個体と交換します。その後、突然変異は全個体に対して確率50%で生じ、どれか1文字がASCIIコードで1だけ変化します。
このあたりのパラメータを変えてみるとなかなか興味深いです。突然変異なしでは、一向に収束しません。突然変異の確率は、高い方が早く収束するようです。世代交代は、ベスト2個体をワースト2個体と交換するだけでも十分で、交叉はあまり収束性に影響しないようです。
]]>永久に自己増殖を繰り返していくセル・オートマトンです。
ルールはなんと219個!、しかも初期状態がかなり複雑です。
またまた、ルールだけを参考に、ncursesを利用して自前で組んでみました。
無限増殖とはいえ、画面には限りがあるので、上辺と下辺、左辺と右辺がそれぞれ繋がっているトーラス状の世界としてみました。
無限に続くので、終了はCtrl-Cによるものとし、シグナルをキャッチして終わるようにしました。
毎世代、画面の全セルごとに、ルールを検索することになるのですが、最初は甘く見て普通のリニアサーチにしてみたら、ループが増殖するに従い、目に見えて画面更新が遅くなっていきます。これは、最初の黒が多い状態ではルールテーブルの最初のあたりで検索が済むのに対し、色のついたセルのルールはテーブルの下方にあるので、検索に時間がかかるためでした。
Linuxではともかく、Cygwinでは待てないほど遅くなります。それに進むにしたがって遅くなる、というのもカッコ悪いです。
そこで、stdlibにあるバイナリサーチbsearchを使ってみました。バイナリサーチはテーブルがソートされていることが前提なので、これまたstdlibにあるクイックソートqsortを使いました。
効果てきめん! 安定して超高速になりました。
//
// langton's loop
// make: gcc lloop.c -lncurses -o lloop
//
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <ncurses.h>
#include <unistd.h>
#include <signal.h>
#define LLOOP_TABLE "lloop.table"
#define TABLE_SIZE 256
#define RULE_SIZE 10
#define BUF_SIZE 256
#define Q_W 15
#define Q_H 10
#define USLEEP 1000
#define VALUE_AT_NO_RULE 0
int **prev, **post;
char table[TABLE_SIZE*4][RULE_SIZE];
int num_of_table = 0;
int width, height;
int debug = 0;
int num_of_search;
void finalizer( void );
void sig_catch( int );
void rotate_rule( char *, char *, int );
int comp( const void *, const void * );
int comp2( const void *, const void * );
void load_table( void );
int search_table( int, int, int, int, int );
void alloc_mem( void );
void def_color( void );
void init_array( void );
void print_usage( void );
// finalizer
void finalizer() {
int i;
// free
for ( i = 0; i < width; i++ ) free( prev[i] );
free( prev );
for ( i = 0; i < width; i++ ) free( post[i] );
free( post );
attrset( COLOR_PAIR(0) );
mvprintw( 1, 0, "Hit any key" );
getch();
endwin();
exit( EXIT_SUCCESS );
}
// signal handler
void sig_catch( int sig ) {
finalizer();
}
// rotate rule
void rotate_rule( char *in, char *out, int rot ) {
int i;
out[0] = in[0];
for ( i = 1; i <= 4; i++ ) {
out[i] = in[(i-1+rot)%4+1];
}
out[5] = in[5];
}
// compare for qsort
int comp( const void *c1, const void *c2 ) {
return strcmp( (char *)c2, (char *)c1 );
}
// compare for bsearch
int comp2( const void *c1, const void *c2 ) {
return strncmp( (char *)c2, (char *)c1, 5 );
}
// load table
void load_table() {
FILE *fp;
char buf[BUF_SIZE];
if ( (fp = fopen( LLOOP_TABLE, "r" )) == NULL ) {
printf("faile open error\n");
exit( EXIT_FAILURE );
}
while ( fgets( buf, BUF_SIZE, fp ) != NULL ) {
if ( isdigit( buf[0] ) ) {
strncpy( table[num_of_table], buf, 6 );
table[num_of_table][6] = '\0';
num_of_table++;
// expand rule
rotate_rule( buf, table[num_of_table++], 1 );
rotate_rule( buf, table[num_of_table++], 2 );
rotate_rule( buf, table[num_of_table++], 3 );
}
}
// sort for bsearch
qsort( table, sizeof(table)/sizeof(table[0]), sizeof(table[0]), comp );
if ( debug ) {
for ( int i = 0; i < num_of_table; i++ ) {
printf("rule: %d %s\n", i, table[i] );
}
}
}
// search table
int search_table( int c, int n, int e, int s, int w ) {
int i;
char *result;
char key[RULE_SIZE];
sprintf( key, "%d%d%d%d%d", c, n, e, s, w );
result = bsearch( key, table, sizeof(table)/sizeof(table[0]), sizeof(table[0]), comp2 );
if ( result == NULL ) {
return( VALUE_AT_NO_RULE );
} else {
return( atoi( result+5 ) );
}
}
// allocate memory
void alloc_mem() {
int i;
prev = (int **)malloc( sizeof(int *)*width );
if ( prev == NULL ) {
printf("memory allocation failed.\n");
exit(EXIT_FAILURE);
}
for ( i = 0; i < width; i++ ) {
prev[i] = (int *)malloc( sizeof(int)*height );
if ( prev[i] == NULL ) {
printf("memory allocation failed.\n");
exit(EXIT_FAILURE);
}
}
post = (int **)malloc( sizeof(int *)*width );
if ( post == NULL ) {
printf("memory allocation failed.\n");
exit(EXIT_FAILURE);
}
for ( i = 0; i < width; i++ ) {
post[i] = (int *)malloc( sizeof(int)*height );
if ( post[i] == NULL ) {
printf("memory allocation failed.\n");
exit(EXIT_FAILURE);
}
}
}
// define color
void def_color() {
start_color();
init_pair( 0, COLOR_BLACK, COLOR_BLACK );
init_pair( 1, COLOR_BLACK, COLOR_BLUE );
init_pair( 2, COLOR_BLACK, COLOR_RED );
init_pair( 3, COLOR_BLACK, COLOR_GREEN );
init_pair( 4, COLOR_BLACK, COLOR_YELLOW );
init_pair( 5, COLOR_BLACK, COLOR_MAGENTA );
init_pair( 6, COLOR_BLACK, COLOR_WHITE );
init_pair( 7, COLOR_BLACK, COLOR_CYAN );
}
// init array
void init_array() {
int i, j;
int init_q[Q_H][Q_W] = {
{ 0,2,2,2,2,2,2,2,2,0,0,0,0,0,0 },
{ 2,1,7,0,1,4,0,1,4,2,0,0,0,0,0 },
{ 2,0,2,2,2,2,2,2,0,2,0,0,0,0,0 },
{ 2,7,2,0,0,0,0,2,1,2,0,0,0,0,0 },
{ 2,1,2,0,0,0,0,2,1,2,0,0,0,0,0 },
{ 2,0,2,0,0,0,0,2,1,2,0,0,0,0,0 },
{ 2,7,2,0,0,0,0,2,1,2,0,0,0,0,0 },
{ 2,1,2,2,2,2,2,2,1,2,2,2,2,2,0 },
{ 2,0,7,1,0,7,1,0,7,1,1,1,1,1,2 },
{ 0,2,2,2,2,2,2,2,2,2,2,2,2,2,0 } };
for ( i = 0; i < width; i++ ) {
for ( j = 0; j < height; j++ ) {
if ( i >= (width-Q_W)/2 &&
i < (width+Q_W)/2 &&
j >= (height-Q_H)/2 &&
j < (height+Q_H)/2 ) {
prev[i][j] = init_q[j-(height-Q_H)/2][i-(width-Q_W)/2];
} else {
prev[i][j] = 0;
}
}
}
}
// print usage
void print_usage() {
printf( "usage: lloop [-dh]\n" );
}
// main
int main( int argc, char **argv ){
int i, j;
int x, y;
int generation = 0;
int c, n, e, s, w;
int opt;
int change = 0;
// option
while ( (opt = getopt( argc, argv, "dh" )) != -1 ) {
switch ( opt ) {
case 'd':
debug = 1; // debug mode
break;
case 'h':
case '?':
default:
print_usage();
exit(EXIT_SUCCESS);
}
}
// set signal handler
if ( SIG_ERR == signal( SIGINT, sig_catch ) ) {
fprintf( stderr, "cannot set signal handler.\n" );
exit( EXIT_FAILURE );
}
// load rule table
load_table();
// init screen
if ( !debug ) {
initscr();
getmaxyx( stdscr, height, width );
} else {
width = 80; height=51;
}
// define color
if ( !debug ) def_color();
// allocate memory
alloc_mem();
// init prev
init_array();
// init position
for ( i = 0; i < width; i++ ) {
for ( j = 0; j < height; j++ ) {
if ( !debug ) {
attrset( COLOR_PAIR( prev[i][j] ));
mvaddstr( j, i, " " );
}
}
}
if ( !debug ) {
mvprintw( 0, 0, "Hit any key" );
refresh();
}
getch();
if ( !debug ) erase();
// endless loop
for (;;) {
for ( i = 0; i < width; i++ ) {
for ( j = 0; j < height; j++ ) {
// set next generation
c = prev[i][j];
if ( j == 0 ) {
n = prev[i][height-1];
} else {
n = prev[i][j-1];
}
e = prev[(i+1)%width][j];
if ( i == 0 ) {
w = prev[width-1][j];
} else {
w = prev[i-1][j];
}
s = prev[i][(j+1)%height];
post[i][j] = search_table( c, n, e, s, w );
if ( post[i][j] < 0 ) {
printf("search faild.\r\n");
exit( EXIT_FAILURE );
}
}
}
// draw
change = 0;
for ( i = 0; i < width; i++ ) {
for ( j = 0; j < height; j++ ) {
if ( !debug ) {
attrset( COLOR_PAIR( post[i][j] ));
mvaddstr( j, i, " " );
}
if ( prev[i][j] != post[i][j] ) change++;
prev[i][j] = post[i][j];
}
}
// not changed
if ( change == 0 ) {
finalizer();
}
// print generation
if ( !debug ) {
attrset( COLOR_PAIR(6) | A_REVERSE );
mvprintw( 0, 0, "%d", generation );
num_of_search = 0;
refresh();
} else {
printf( "%d %d\n", generation, num_of_search );
num_of_search = 0;
if ( generation >= 500 ) exit(EXIT_SUCCESS);
}
generation++;
//usleep( USLEEP );
}
// finalize
finalizer();
}
実行するとこんな感じ。 カラーでにぎやか。わっかがぐるぐる回りながら上下左右に増えていきます。
真っ暗な世界を増殖する場合はいいんですが、自分の残骸などに衝突すると、ルールに記載のないパターンになることがあるみたいで、その場合は死ぬ(黒くなる)ようにしてみたら、最後に残骸だけ残って停止します。
]]>もっと興味深いのは、10000ステップを過ぎたあたりから、それまで混沌(カオス)としていた足跡が、急に規則正しく変化して、右下に一直線に走って行ってしまうように変化することです。
「カオスの縁」というのだそうですが、局所的な単純なルールから、全体的には混沌が生じ、その混沌から突然秩序が生じるわけです。なぞです。
これまた、ルールだけを頼りに自前でコーディングしてみました。またncursesを使いましたが、アリにはもっと広いフィールドが必要な感じです。ncursesだとどうしても表示の分解能が文字単位になってしまうので粗いです。
前回のライフゲームでは、フィールドの大きさを #define で決めうちにしていたのですが、今回は、ターミナルの大きさいっぱいを使うようにしました。起動時のターミナルサイズを取得しmallocしました。アリがフィールドから飛び出るとプログラムが止まるようにしました。
あと、Linuxではループごとにusleepで少し待たないとあっという間に終わってしまうのですが、Cygwinでは何秒に指定しようがusleepを呼ぶだけのオーバーヘッドが数十msくらいある感じで非常に遅いです。下の例ではusleepをコメントアウトしています。
//
// langton's ant
//
#include <stdlib.h>
#include <ncurses.h>
#include <unistd.h>
#define USLEEP 1
int **array;
int width, height;
enum DIR { NORTH, EAST, SOUTH, WEST };
void alloc_mem( void );
void def_color( void );
void init_array( void );
// allocate memory
void alloc_mem() {
int i;
array = (int **)malloc( sizeof(int *)*width );
if ( array == NULL ) {
printf("memory allocation failed.\n");
exit(EXIT_FAILURE);
}
for ( i = 0; i < width; i++ ) {
array[i] = (int *)malloc( sizeof(int)*height );
if ( array[i] == NULL ) {
printf("memory allocation failed.\n");
exit(EXIT_FAILURE);
}
}
}
// define color
void def_color() {
start_color();
init_pair( 0, COLOR_WHITE, COLOR_BLACK );
init_pair( 1, COLOR_BLACK, COLOR_WHITE );
}
// init array
void init_array() {
int i, j;
for ( i = 0; i < width; i++ ) {
for ( j = 0; j < height; j++ ) {
array[i][j] = 0;
}
}
}
// main
int main(){
int i;
int x, y;
int counter = 0;
enum DIR dir;
// init screen
initscr();
getmaxyx( stdscr, height, width );
// define color
def_color();
// allocate memory
alloc_mem();
// init array
init_array();
// init position
x = width / 2;
y = height / 2;
dir = NORTH;
mvprintw( y, x, "Hit any key" );
getch();
erase();
// endless loop
for (;;) {
if ( array[x][y] == 1 ) {
array[x][y] = 0;
attrset( COLOR_PAIR(0) );
mvaddstr( y, x, " " );
switch ( dir ) {
case NORTH:
dir = EAST;
x = x + 1;
break;
case EAST:
dir = SOUTH;
y = y + 1;
break;
case SOUTH:
dir = WEST;
x = x - 1;
break;
case WEST:
dir = NORTH;
y = y - 1;
break;
}
} else {
array[x][y] = 1;
attrset( COLOR_PAIR(1) );
mvaddstr( y, x, " " );
switch ( dir ) {
case SOUTH:
dir = EAST;
x = x + 1;
break;
case WEST:
dir = SOUTH;
y = y + 1;
break;
case NORTH:
dir = WEST;
x = x - 1;
break;
case EAST:
dir = NORTH;
y = y - 1;
break;
}
}
// judge overflow
if ( x < 0 || y < 0 || x > width-1 || y > height-1 ) {
break;
}
// draw
mvprintw( 0, 0, "%d", counter );
refresh();
counter++;
//usleep( USLEEP );
}
// finalize
attrset( COLOR_PAIR(0) );
mvprintw( 1, 0, "Hit any key" );
getch();
endwin();
return(0);
}
ふと思い立って、Cで実装してみました。ネットに溢れている実装例は見ていないので、正解ではないでしょうが、それっぽく動きます。ncursesを試しに使ってみたらきれいにできました。
//
// life
//
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <ncurses.h>
#include <string.h>
#define XSIZE 40
#define YSIZE 20
#define DIVBY 5
#define USLEEP 500000
#define MESSAGE "hit any key to exit"
int prev[XSIZE][YSIZE], post[XSIZE][YSIZE];
// initialize array
void init_array() {
int i, j, r;
srand( (unsigned)time( NULL ) );
for ( i = 0; i < XSIZE; i++ ) {
for ( j = 0; j < YSIZE; j++ ) {
r = rand() % DIVBY;
if ( r != 0 ) {
prev[i][j] = 0;
} else {
prev[i][j] = 1;
}
}
}
}
// judge dead or alive
int dora( int sum, int now ) {
if ( now == 1 ) {
if ( sum == 2 || sum == 3 ) {
return(1);
} else {
return(0);
}
} else {
if ( sum == 3 ) {
return(1);
} else {
return(0);
}
}
}
// change generation
void chgen() {
int i, j;
int z;
for ( i = 0; i < XSIZE; i++ ) {
for ( j = 0; j < YSIZE; j++ ) {
if ( i == 0 && j == 0 ) {
z = prev[i+1][j] + prev[i+1][j+1] + prev[i][j+1];
} else if ( i == XSIZE-1 && j == YSIZE-1 ) {
z = prev[i-1][j] + prev[i-1][j-1] + prev[i][j-1];
} else if ( i == 0 ) {
z = prev[i][j-1] + prev[i][j+1] \
+ prev[i+1][j-1] + prev[i+1][j] + prev[i+1][j+1];
} else if ( i == XSIZE-1 ) {
z = prev[i][j-1] + prev[i][j+1] \
+ prev[i-1][j-1] + prev[i-1][j] + prev[i-1][j+1];
} else if ( j == 0 ) {
z = prev[i-1][j] + prev[i+1][j] \
+ prev[i-1][j+1] + prev[i][j+1] + prev[i+1][j+1];
} else if ( j == YSIZE-1 ) {
z = prev[i-1][j] + prev[i+1][j] \
+ prev[i-1][j-1] + prev[i][j-1] + prev[i+1][j-1];
} else {
z = prev[i-1][j-1] + prev[i-1][j] \
+ prev[i-1][j+1] + prev[i][j-1] + prev[i][j+1] \
+ prev[i+1][j-1] + prev[i+1][j] + prev[i+1][j+1];
}
post[i][j] = dora(z, prev[i][j]);
}
}
}
// copy array
void copy_array() {
int i, j;
for ( i = 0; i < XSIZE; i++ ) {
for ( j = 0; j < YSIZE; j++ ) {
prev[i][j] = post[i][j];
}
}
}
// judge loop
int isSilence() {
int i, j;
int z = 0;
for ( i = 0; i < XSIZE; i++ ) {
for ( j = 0; j < YSIZE; j++ ) {
if ( prev[i][j] != post[i][j] ) {
z++;
}
}
}
return(z);
}
// main routine
int main() {
int i, j;
int x, y, w, h;
int gen;
// initialize screen
initscr();
getmaxyx( stdscr, h, w );
// initialize array
init_array();
erase();
for (;;) {
gen++;
x = w / 2;
y = ( h - YSIZE ) / 2 - 2;
move(y, x);
printw("%d", gen);
for ( i = 0; i < XSIZE; i++ ) {
for ( j = 0; j < YSIZE; j++ ) {
// display
x = ( w - XSIZE ) / 2 + i;
y = ( h - YSIZE ) / 2 + j;
move(y, x);
if ( prev[i][j] == 0 ) {
addch('.');
} else {
addch('@');
}
}
}
refresh();
// change generation
chgen();
// jedge silence
if ( 0 == isSilence() ) {
break;
}
copy_array();
usleep( USLEEP );
}
x = w / 2 - strlen( MESSAGE ) / 2;
y = h / 2 + YSIZE / 2 + 2;
move(y, x);
printw( MESSAGE );
timeout(-1);
getch();
endwin();
return(0);
}
動かすとこんな感じ。
ずっと見ていられます。
]]>少し前に流行ったらしいのですが、プログラミングのセンスを試すのに課される問題のようです。
上記のページに様々な言語での実装例が載っています。そこになかった、awkで実装してみました。
BEGINブロックのみというawkにあるまじき実装ですが。
#!/bin/awk -f
# zunzoko.awk
BEGIN{
srand()
for (;;) {
if ( 0 == int((rand()*10)%2) ) {
z++
printf("ズン ")
} else {
printf("ドコ")
if ( z == 4 ) {
printf(" キ・ヨ・シ!\n")
break
}
z = 0
printf("\n")
}
}
}
動かすとこんな感じ。
ズンが4回より多く続いた場合はキヨシコールしないようにしたので、間抜けにズンが長い場合があります。
]]>CASIOのfx-JP900です。液晶が高精細なのが、これまでの電卓にはなかった特徴です。高精細とは言ってもスマホなどに比べれば全然ですが。
正直、これらの関数電卓はもう高機能すぎて、ほとんどの機能は使いません(断言)。自分の使いたい機能が使いやすく実装されているか、につきるわけです。
右に写っているCannonのF-766Sは、他の製品にない特徴があって、-9乗のn(ナノ)や、6乗のM(メガ)など、電気屋が多様する単位が2アクションで入力できます。表示もできます。CASIOもできるけど、入力も表示も手間が2、3倍掛かって使う気がしない。CASIOのテンキーには機能がアサインされていないキーがあるので、ぜひここに実装してほしいところです。Shift-3で3乗のk、Alfa-3で-3乗のm、6にuとM、9にnとG、2にpとT、5にfとP、とアサインすれば2アクションで入力できるし、覚えやすいじゃないですか。
左に写っているSHARPは、16進数の入力や計算が他メーカより簡単にできるので購入しました。その後、16進数計算はあまり使わなくなってしまったので、今回、CASIOを買いました。
液晶は確かにきれいなのですが、使っていてうれしかったのは、薄く軽くなっていることです。今の技術なら、もっともっと薄く軽くできるのにって、ずっと思っていたんです。まあ、コストとの兼ね合いなのでしょうが。ただし、机に置いて使うと、歪んでいるのか、カタカタして使いにくい。
少々高くてもいいから、スマホの実装技術を駆使して、かっちりと薄く、キーも固く、液晶も超高精細で、というのを作れませんかね。いや、作れるんでしょうが、売れないんでしょうね。
とにかく、各社のいいとこどりをしたベストな関数電卓がほしいです。
]]>子供が持って行ってしまっているので、最後の2巻分(上下で4冊)しか写真はありませんが、数年かけて全12巻(24冊)揃えましたよ。長かった。
岩波少年文庫は本当に名作ぞろいで、小中学校のうちにすべて読破すべき、と思うくらいです。ランサム・サーガを知ったのは残念ながら大人になってからだったので、少年時代に読みたかった、と思いました。
きっとコアなファンがたくさん解説していらっしゃると思いますので、 簡単にしか紹介しませんが、少年少女のありようが詰まっていて、忘れていた子供の頃の気持ちを思い出すことのできる、稀有な作品です。
また、ヨットが重要なモチーフとなっており、その技術的な面や船乗りとしての規律など、私には特に興味深く感じられました。
小学生のお子さんにぜひ勧めてください! 旧訳ならどこの図書館にも揃っています。
気になった方は、作者「アーサー・ランサム」や、このシリーズの第1作「ツバメ号とアマゾン号」などで検索してみてください。
]]>3Dプリンタで作ったものを初めて触りましたが、積層面も結構きれいにできるのにびっくり。もっとでこぼこかと思っていました。
オレンジ色のにくいやつ!
]]>ところが、これが一筋縄では行きませんでした。ホームビデオのデータのバックアップに一番手間取ったのですが、関係ないところは省略して、Kodi関係の備忘録的に大切なところだけメモしておきます。
dos charset = CP932
unix charset = UTF-8
display charset = UTF-8
dos filemode = Yes
dos filetimes = Yes
dos filetime resolution = Yes
と、いくつか気になる点はあるものの、昔からの写真やホームビデオが気軽にテレビで見られるようになったので、家族には好評でした。
]]>
そして、最近発見したのですが、イオンでプライベートブランドのミント菓子が¥78で売っていました。ケース形状やミントタブレット形状などはFRISKそっくりですが、ケースは並べてみるとイオンのほうが細長いです。そして、このイオンのはなんと詰め替え用も売っているとのこと。
ところで、FRISKケースは電子工作のケースとして再利用することは、半ば定番化してきていますが、遅ればせながら、やってみました。
工作というほどのものでもないですが、レーザーポインタを作ってみました。
右下手前にある黒い円筒形のものが3Vで動く赤色レーザーモジュールです。
左下手前にあるのがタクトスイッチ。奥にあるのがCR2032ボタン電池です。
部品はいずれもamazonで購入(スイッチは手持ちだったので入手先は失念)しました。部品代は総額¥250くらいでしょうか。
配線をはんだづけして、部品の固定は瞬間接着剤です。穴あけは小型電動ドリルで。ちょっとひび割れが入ってしまいました。
ふたをスライドすることで、タクトスイッチが顔を出して押せるようになる構造です。カバンの中などで意図せずスイッチがオンになるのを防ぐためです。
ちょっと手間取った点は、タクトスイッチの高さが高すぎてケースに収まらなかったこと。ボタン部分をカッターで削りました。電池ホルダもギリギリです。心配していたレーザーモジュールはぴったりだったのに、ノーケアだった部品に足元をすくわれました。
出来栄えは、まあまあ満足。レーザーは決して明るくはないですが、暗い部屋でスクリーンに近ければプレゼンで使えるレベルです。
できればもっと小型化したいところです。ボタン電池をもっと小型のものにして、円筒状のケースに入れたいですね。
]]>オードリー・ヘプバーンがチョコのCMに出ています。
あまりに自然だし、風景や車や服装も50年代っぽいし、「へー、こんなCMに出てたんだ」などと普通に思ってしまいましたが。
ちょっと待てよ。と。
本人はファッションからも20代の頃のオードリーだ。「ローマの休日」や「サブリナ」の頃で、これらの映画はモノクロ。当時カラーもあったけど、こんなにきれいじゃないはず。
しかもBGMは、本人が歌っているムーン・リバーじゃないか。ムーン・リバーは「ティファニーで朝食を」の主題歌で劇中で歌っているから、それを使っているようだが、ティファニーの頃はオードリーは30代半ば。
おかしい。
って思ったら、コメント欄にCGだと書いてありました。なーんだ。
でもとても自然に出来ていて、本当に映画のワンシーンのようでした。素晴らしい。
ほかの女優さん版も見てみたいですね。グレース・ケリーとか。
]]>レポートなど、ふつうはA4サイズの文書として作成しますが、大きな図面を貼る場合など、時にA3を横にしたページを差し挟みたくなります。そのやり方は、ネットでもよく見かけますので、ここでは省略(ページ区切りを挿入した上で、ページサイズ変更)。
ヘッダ、フッタを設定している場合、ページサイズをA3ヨコに変更しても、ヘッダがA4のままになります。
これがどうにかならんものかと、ネットで調べましたが見つけることができませんでした。WORDのテンプレートなどを調べた結果、まあまあ満足のいくやり方が見つかったので、メモ代わりに書いておきます。
特に個人的な好みのヘッダは、一本横線を入れて、その右上に日付などを書いたものです。
このように、2ページ目のA3ヨコでもヘッダが1ページ目と同じになっています(この図は比べ安くするために、ページの縦方向を短くしています)。
右上の日付はテキストボックスを置いていますが、これは、レイアウトの設定で、右揃えに配置することでページサイズによらず対応できます。
横線は図で描いているのですが、どうにもならないようです。
そこで、横線は、表の罫線を利用することにします。
表の罫線の下側だけ表示しています。このままではまだページサイズに対応できていません。
表のプロパティで、表のサイズを「100%で固定」します。デフォルトではサイズはcm単位ですが、%単位での指定も可能なのでした。
さらに、2ページ目のヘッダの「前と同じ」を解除すれば、上のようにページサイズに対応できます。
日付はテキストボックスでもいいですが、表のセルの中に右揃えで書いてしまえば簡単です。
横線は、表のほかにも、罫線メニューから挿入できる「横線」や、ハイフンを3連続で入力して2連続リターンするとオートコレクトで変換される横線(正体不明)も100%にできます。
]]>もう20年近く洋楽をちゃんと聞かなくなっていましたが、何きっかけか忘れましたが、Bruno Marsを知りました。
最初に聞いたのはこれ。
なんか懐かしい雰囲気。私が洋楽をよく聞いていた80年代のファンクの香りがします。
次に知ったのがこれで、もろファンク。
Bruno Marsって超売れっ子みたいですね。全然知りませんでした。
このバンドメンバーと踊りながらパフォーマンスする感じも、80年代ファンクバンドっぽくて好きです。複雑なダンスを踊っているわけではないですが、超かっこいいです。
◆Jam & Lewis の思い出
高校生の頃、洋楽を聞き始めて、初めにはまったのは Prince でした。当時は Purple Rain の頃で、Prince 絶頂期と言っていいのでは。1999、Purple Rain、around the world in a day の3枚は凄い。
Princeの曲を紹介し出すとキリがないですが、これがいいかな。
Princeは曲はすごいんですが、正直、見た目やパフォーマンスは生理的に気持ち悪いです。ファンの方、ごめんなさい。私もファンなんですよ、一応。
Prince にもファンクっぽいのはあるんですが、Princeの息のかかったバンドで The Time というのがありました。映画Purple Rainに出演して有名になったのですが、その後に出た pandemonium というアルバムに好きな曲が多くて、お気に入りになりました。Price が陰とすれば The Time は陽って感じ。
で、The Timeがファンクっぽい曲が多い。といっても本当のファンク好きにはゲテモノ扱いかもしれません。Prince同様、ギターロックも入ってるし、ギラギラしたシンセサウンドもてんこ盛りですから。
後で気が付くと、これが Jam & Lewis サウンドだったんですな。当時は知りませんでした。
The Time の動画は少ない。頭が切れてますが、これで。
その後、ジャネット・ジャクソンがコントロールとリズムネイションでブレイクした時、この感じ好きかも、って思い、この時初めてプロデューサーに目が行きました。Jam & Lewis、The Time のキーボードとベースです。
これがリズムネイション。
その後も、キャリン・ホワイトのロマンティックって曲がいいなって思ったら、やっぱり Jam & Lewis で、ああ、俺ってこの人たちの曲が好きなんだ、とその時確信しました。
これがロマンティック。
いまでも基本踊れる曲が好きですし、好みの曲の条件のうちの、シンプルでキャッチーなフレーズが単純に繰り返されるサビ、サビとは違うパターンのリフがあること、などは、このころの体験がベースにあるような気がします。
最後に、文脈とはあまり関係ないですが、YouTube見てたら見つけて懐かしいので貼っておきます。当時大好きだったなぁ。
SPICEネットリストはサブサーキット(SUBCKT)という回路ブロックが階層構造をとっています。Cプログラムが関数によって構造化されているのと似ています。
SPICEネットリストを一見しただけでは、SUBCKTの階層構造を把握することは困難です。どのSUBCKTがどのSUBCKTを呼んでいるのか、何度もネットリストを読み返さなければなりません。
そこで、階層構造といえば「再帰呼び出し」です! コンピュータのファイルシステムの階層構造などを表示しようとすると再帰呼び出しを使います。ある関数が、その中で自分自身を呼び出す、というものです。
ファイルシステムの場合、何度も同じフォルダの中身をスキャンすることができますが、SPICEネットリストをシェルやawkで取り扱う場合、何度も読み直すことは困難です。
そこで、少し美しくないですが、SUBCKTごとに中身をファイルに書き出しておくことにしました。ファイル名をSUBCKT名にしておくことで、何度でも中身をcatすることとします。
できあがったのが以下です。以前紹介した、継続行処理を前処理として入れています。
#!/bin/sh]]>
# cdltree -- print tree of cdl
# set input cdl file to INPUT variable.
# create SUBCKT_LIST of subckts include input cdl file.
# v1.0 /HORI @2015-07-15
#######################
# VARIABLE DEFINITION #
#######################
INPUT=./input.cdl
SUBCKT_LIST=./subckt_list
LEVEL=1
TOP_CELL_NAME=""
#######################
# FUNCTION DEFINITION #
#######################
# create subckt files
function mksubcktfiles() {
if [ -f $SUBCKT_LIST ]; then
rm -f $SUBCKT_LIST
fi
cat $INPUT | awk '
# de-plus
/^\+ /{
LINE = LINE substr($0, 3)
next
}
{
if (LINE != "") {
print LINE
}
LINE = $0
}
END{
}' | awk -v SUBCKT_LIST=$SUBCKT_LIST '
# create all subckt files except for primitive
/^\.SUBCKT/{
flg = 1
file_name = $2
print $2 >> SUBCKT_LIST
}
/\.ENDS/ && flg == 1{
flg = 0
}
flg == 1 && /^XI/{
print $NF > file_name
}'
}
# sort subckt file's contents
function sort_file() {
for FILE in `cat $SUBCKT_LIST`
do
if [ -f $FILE ]; then
cp -f $FILE tmp
cat tmp | sort | uniq > $FILE
fi
done
rm -f tmp
}
# get top cell name
function get_top_cell_name() {
TOP_CELL_NAME=`cat $INPUT | awk '
/^\* Top Cell Name:/{
print $5
}'`
echo "$TOP_CELL_NAME ($(( LEVEL - 1 )))"
}
# search tree
function subckt() {
TARGET=$1
if [ -f $TARGET ]; then
for ELEMENT in `cat $TARGET`
do
# create indent space
COUNT=$LEVEL
while (( COUNT-- ))
do
echo -n " "
if [ $COUNT -le 0 ]; then
break
fi
done
echo "$ELEMENT ($LEVEL)"
LEVEL=$(( LEVEL + 1 ))
subckt $ELEMENT # recursive call
done
# end of list, return upper level
LEVEL=$(( $LEVEL - 1 ))
else
# file not found, that element is primitive, reset level
LEVEL=$(( $LEVEL - 1 ))
fi
}
# post processing
function post() {
for FILE in `cat $SUBCKT_LIST`
do
rm -f $FILE
done
}
################
# MAIN ROUTINE #
################
trap post 0 1 2 3 15 # signal 0 is normal exit
mksubcktfiles
sort_file
get_top_cell_name
subckt $TOP_CELL_NAME
#EOF
これが、シェルやawkで処理する際に障壁になります。
そこで、前処理として、「+」で始まる行を前の行にくっつけて1行にしてしまうことにします。
シェルやawkが扱える1行の文字数には上限があります(昔のSunOSでは1024文字だったような)が、面倒になるのでここでは無視します。使用上の制約としておきます。
出来上がったawkスクリプトが以下です。attachという名前にしました。なかなかシンプルで気に入っています。もっとシンプルにできるものでしょうか。
#!/bin/awk -f]]>
/^\+ /{
LINE = LINE substr($0, 3)
next
}
{
if (LINE != "") {
print LINE
}
LINE = $0
}
END{
}
でも、メディアサーバって何?って感じでした。もともとNASやサーバに写真、動画、音楽などの情報ファイルを集中管理している人は、それらをリビングのテレビでブラウジングして視聴可能になるので、相当利便性が上がると思います。私はそんな事をしていないので、まあ、何ができるかやってみよう、という感じです。
◆HDMIの解像度設定
リビングのテレビでの表示が不調(たまに映らなかったり、ちらついて安定しなかったり)なので、ちゃんと設定することにしました。テレビのマニュアルを調べたところ、HDMI入力で対応可能な解像度の選択肢が載っていました。ラズパイが設定可能な解像度はこちらのページの中程。両方を見比べてワイド画面にピッタリなWXGAの1360×768を選択しました。
ラズパイの設定は/boot/config.txtで。hdmi_group=2、hdmi_mode=39、としました。
結果、ばっちり安定して映るようになりました。
◆kodiのインストール
基本はbe-damaさんのサイトを参考にしました。ここでは補足情報だけ追記します。
ラズパイをHDMIケーブルでテレビに繋ぎ、キーボードとマウスをUSBに繋ぎました。kodiをインストールして自動起動設定をして再起動すると、kodiが起動しました。でもキーボードもマウスも効かなくて操作できません。kodiはkodiというユーザで起動されているのですが、kodiユーザがinputグループに属していないためにこうなるそうです。なので、以下で解決。
pi@raspberrypi ~ $ sudo usermod -a -G input kodi
◆kodiの操作
せっかくテレビにkodiの画面が出ているのに、操作がキーボード&マウスでは不便です。なんとテレビのリモコンでkodiの操作が可能です。初めて知ったのですが、HDMIというのは単に映像と音声を送るだけでなく、テレビのリモコンの操作情報をソース側へ渡すこともしてくれるそうで、kodiも対応しています。kodiのメニューの、
システム > 設定 > システム > 入力デバイス > 周辺機器
で有効化します。
ただし、操作できる機能はテレビにより異なるようで、うちのSHARP製では、方向キー、戻るキーくらいしか効きませんでした。これでも最低限の操作は可能です。
他の選択肢として、Androidアプリのリモコンがあります。いくつか試した中ではYasteが完璧です。スマホやタブレットから完璧に操作できます。テレビリモコンと違ってLAN越しですが遅延は全く気になりません。テレビリモコンでは機能しなかったホームボタンやコンテキストメニュー表示なども機能します。さらに検索などの文字入力が、kodiのソフトキーボードでは英語のみなのですが、リモコンから日本語を送信できます。
◆YouTubeが見られる
kodiには非常に多数のアドオンが用意されています。kodiにレポジトリを登録し、インターネットからダウンロードしてインストールします。その中に、YouTubeを視聴できるアドオンがあったので試してみました。いちいちPCを起動しなくてもテレビでYouTubeが見られれば便利そうです。
ビデオのアドオンからYouTubeを選択してインストールするだけ利用可能になりました。サムネイル表示はかなりもっさりしています。また、前述のとおり検索に日本語を使うにはアプリリモコンを使うしかないです。ただ、一度検索した語句はメニューに登録されるので次回は入力不要です。
◆kodiから普通のアプリケーションを起動できないか
kodiの裏ではLinuxが走っているわけですから、Webブラウザを起動してリモコン操作できれば、テレビでgoogle検索できて便利です。ですが、Xは起動していないし、リモコンでマウスポインタが動かせるか、などそもそもできるかどうかわかりません。
調べると、汎用ラウンチャとして Advanced Launcher なるアドオンがあります。いろいろ試していますが、未だアプリケーションの起動に成功していません。あらかじめXを起動しておいて、Xauth許可やディスプレイ設定などして…など試行錯誤中です。
アドオンやkodiの設定は pythonやxmlで記述されているようで、学習すればカスタマイズは比較的容易かもしれません。 ]]>繋いでいないのは、リビングに有線LANが来ていないからです。我が家では2FにNuroひかりのモデム?が置いてあって、そいつが唯一の有線HUB兼無線親機です。
ラズパイには有線LANポートがあり、今はUSBに無線LANアダプタを付けているので、ラズパイをブリッジにしてテレビをネットに繋げられるのでは? と思いました。やってみましょう。
◆その前にSDカード延命作戦
フラッシュは書き換え回数が有限ですので、Linuxのようにいろいろなファイルを書き込むOSを連続運転しては、SDカードがわりと短期間で使用不能になると思います。
そこで、スワップファイル無効、一時ファイル置き場をRAM(tmpfs)にする、などの措置で、SDカードへの書き込みを減らします。
kimura@pc-links.comさんの「Raspberry Pi でRAMディスクを使う」の通りやればできますので、ここでは詳細省略します。
◆ブリッジ作成
参考にさせて頂いたのは、tenforwardさんの「Raspberry Pi を無線 LAN アクセスポイントに」です。こちらを始め、いくつかのサイトでは無線LANアクセスポイント化する話が多いですが、今回やるのはその手前の有線と無線をブリッジするところまでです。
pi@raspberrypi ~ $ sudo apt-get install bridge-utils
auto br0
iface br0 inet static
address 192.168.1.10
netmask 255.255.255.0
gateway 192.168.1.1
bridge_ports wlan0 eth0
うまくいったのですが、いくつか疑問が残っています。