FM牛鍵屋本舗

プログラマ(弱)の日々精進系ブログ

【再読】プログラマの数学 第2版 第2章

プログラマの数学 第2版

プログラマの数学 第2版

プログラマ脳を鍛える数学パズル」に
手も足もでないので勉強しなおします。

第2章 論理

  • 論理は正確に物事を記述する道具

キーワード

  • 命題
    • 恒真命題
  • 真/偽

網羅的で排他的な分割

  • 「もれ」も「だぶり」もない状態

演算の種類

ツール

  • 数直線
  • 真理値表
  • ベン図
  • カルノー
    • 3つの命題のときの書き方が特徴的

未定義を含む論理

  • undefined
    • リレーショナルモデルにおけるNullに近い?
    • JavaScriptにはまんま存在する

ド・モルガンの法則と双対性

全体的にリレーショナルモデルの考え方にちかい、というか リレーショナルモデルが述語論理のうえに成り立っている、のか。

SpringBoot + H2DB

build.gradleに追記する

  runtimeOnly 'com.h2database:h2'
  implementation 'org.springframework.boot:spring-boot-starter-jdbc'

application.propertiesに追記する

# 接続先 実行するディレクトリを基準とした相対パス
# Eclipseからbootしたらプロジェクトルート直下にh2dbディレクトリが出来る
spring.datasource.url=jdbc:h2:./h2db/sample
# ユーザー名
spring.datasource.username=sa
# パスワード
spring.datasource.password=
# Webコンソールを有効にするか
spring.h2.console.enabled=true
# Webコンソールのパス
spring.h2.console.path=/h2-console
# remoteアクセスを有効にするか
spring.h2.console.settings.web-allow-others=false
# Driver名。h2のjarに含まれている
spring.datasource.driverClassName:org.h2.Driver

SpringBootを起動する

`http://localhost:8080/h2-consoleにアクセスする

設定通りコンソールにアクセスする

schema.sqlとかをクラスパスにおいておけば起動時にデフォルトで入れてくれるのかな…??

Mac+Intellijで"Created by ${USER}"を変更するよ!!

うっかりリアルイニシャルが記載されたままGistにあげてしまったので変更方法をメモするよ。

/Applications/IntelliJ IDEA CE.app/Contents/binidea.vmoptionsというファイルがあるので

これに-Duser.name=${変えたい名前}を追記してIntellijを再起動するよ。

stackoverflow.com

Javaでファイルを読むよ、でもこれは覚書だよ

Java8の試験でやったはずなんですけど、

すっかり忘れてましたね。

Files.linesでStreamを返せます。

でも、何度検証してみても

BufferedInputStream is = BufferedInputStream(FileInputStream(new File("/nhome/hoge.list")));

ByteArrayOutputStream os = new ByteArrayOutputStream();

byte[] bytes = new byte[1024 * 10];
int res = -1;
while ( (res = is.read(bytes, 0, bytes.lengh())) != -1) {
  os.write(bytes, 0, res);
}
String result = new String(os.getByteArray(), StandardCharset.UTF_8);

これの方が高速なんだ…うーむ。

Windowsでシンボリックリンクを作るよ!!

エディタはkaoriyaさんのところのvim(gvim)を使っています。

最近、grepをvimgrep(遅い)から変えようとjvgrepを入れました。

github.com

既存ソースの調査をしなければならなくなり、ふと思いました。

Eclipseでいうワーキングセットみたいなディレクトリ作っといたら楽じゃないか、と。

そのためには…そうだ、シンボリックリンクを作ろう!!Windowsだけど!!

管理者モードでコマンドプロンプトを立ち上げて

mklink /D <作成するリンク> <対象ディレクトリ>

Dスイッチはディレクトリを対象にするものです。

また、他にも

  • J : ジャンクション(簡易版のディレクトリリンク、ソフトリンク)
  • H : ハードリンクを作成する。管理者モードでなくても使用できる。

などがあります。

しめしめ、これでワーキングセットが作れてウハウハやで…

と、思っていたら、jvgrepはシンボリックリンクディレクトリとして解釈しないようです。 具体的に言うと、go言語の FileInfo.IsDirectoryでtrueを返さない。

がっかり。

落としてきてその部分を修正してビルドするのも手だけど 一旦諦めてjvgrepのドキュメントをよく読むと

あ、検索対象、可変長だ

:jvgrep "regex pattern" ./hoge/**/*.* ./moge/**/*.*

一旦はこれで…

"Web開発者のための大規模サービス技術入門"を読んだよ

"Web開発者のための大規模サービス技術入門"を読んだよ

紹介

Web**+DB Press Plusの持ちやすいサイズ。 はてなの方が書かれた書籍で、インターン研修をベースにしているようです。

インターンレベル高っ。

実際のはてなや他の大規模サービスの事例をもとに、 インフラやアプリケーションが大規模サービスを構築する際に 考慮するべきことがわかりやすくかかれています。

また、一部アルゴリズムやツールの紹介もあって楽しく読み通せました。

10年前くらいの本なので、クラウド周りの状況が変わっているかな、とは思いますが、 そのあたりにも触れています。ELBが出た当時。

雑感

はてなの人レベル高っ。

もともと大規模なシステム関わったことがなく、そこそこのエンタープライズシステムばかりで ある程度のリソース負荷であればほとんど考えてこなかったのですが、 この本で紹介されている「圧縮」とか「検索エンジン」とかは流用できるなー。

相変わらず計算量オーダーのlogがよくわからない…。

それにしてもわくわくしてなにか作りたくなる気分になる一冊でした。

メモ

  • ディスクI/Oを減らし、極力オンメモリにする
  • DBの負荷分散、冗長化
    • テーブルの役割毎に異なるホストへ分割(論理的、派生して参照中心か更新があるか)
    • 複数HOSTにまたがるのでJOINを避ける
  • ロードアベレージ重要
  • 用途特化型のインデクシング
  • 圧縮
  • ベイジアンフィルタ
  • Trie
  • AC法
  • システム安定化に対する「地雷」
    • 特定条件下で発動
  • システム安定化に関わる「自律化」

アルゴリズムとOS、ジョブキューとキャッシュについて調べたくなりました。どうしても。

# Javaで簡易テンプレートを置換するよ!!

Javaで簡易テンプレートを置換するよ!!

仕事でテンプレートのプレースホルダーっぽいのを置換することになりました。

~テンプレートエンジン使えよ~

プレースホルダーはある特定の文字列から始まり、特定の文字列で終わります。

置換する文字列は元を辿れば画面入力、なので上記の特定の文字列が入力されても置換されないようにする必要があります。

そのため、単純なreplaceではだめで、どうしようかなーと考えた結果、一旦プレースホルダーを残したままsplitで配列化したあとに、ループ処理で置換することにしました。

~最初の仕様では置換箇所一つだったから問題なかったのに…~

さあどうやってsplitしてやろうか!!

apache-commonsのStringUtils

困ったときのStringUtils。

JavaDocを舐めてみましたが、置換文字列を残すのは難しそう。

java.util.StringTokenizer

コンストラクタだけで仕様満たせるとか神ってる…!!と思ったら

StringTokenizerは、互換性を維持する目的で保持されているレガシー・クラスであり、新規コードでは使用が推奨されていません。この機能の使用を考えているなら、Stringのsplitメソッドまたはjava.util.regexパッケージを代わりに使用することをお薦めします

Oops.

java.lang.String

ずばりそのものはない。

正規表現で後読みとか駆使すれば出来なくはない(と思う)けど、これはまたの機会に。

力技

もともとテンプレートエンジン使えよってところからスタートしているので、どう転んでも車輪の再発明

それなら力技でやってやる…!!

インターフェース

すでにプレースホルダーが複数Enumに定義されていたのでインターフェース作成。

後述の通り、こいつが開始文字列と終了文字列を持っていてもいいのかもしれない。

package sample.template;

public interface ReplacementHolder {
     String getReplacement();
}

置換文字列の実態を保持しているEnum

package sample.template;

public enum PlaceholderName implements ReplacementHolder {
    /** 猫の名前 */
    CAT_NAME("#{catName}#")
    /** 犬の名前 */
    , DOG_NAME("#{dogName}#")
    ;
    /** コンストラクタ */
    PlaceholderName(String replacement) {
        this.replacement = replacement;
    }
    private String replacement;
    public String getReplacement() {
        return this.replacement;
    }
}

分割した各トークンを表すクラス

package sample.template;

public class Token {

    /** トークン */
    private String token;

    /** 置換対象か */
    private boolean replaces;

    /** コンストラクタ */
    public Token(String token) {
        this(token, false);
    }

    /** コンストラクタ */
    public Token(String token, boolean replaces) {
        this.token = token;
        this.replaces = replaces;
    }

    /** 更新 */
    public void update(String token) {
        if (this.replaces && token != null) {
            this.token =  token;
        }
        this.replaces = false;
    }
    public boolean replaces() {
        return this.replaces;
    }
    public boolean notReplaces() {
        return !this.replaces;
    }
    public String getToken() {
        return this.token;
    }
}

実際に処理するクラス

package sample.template;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class Replacer<T extends Enum<?> & ReplacementHolder >{

    /** 開始マーカー */
    private String startMarker;
    /** 終了マーカー */
    private String endMarker;

    /** コンストラクタ BeanValidatorとかで契約的にしたほうがよいかも */
    public Replacer(String startMaker, String endMarker) {
        if (startMaker == null || endMarker == null) {
            throw new IllegalArgumentException("不正な引数");
        }
        this.startMarker = startMaker;
        this.endMarker = endMarker;
    }

    /** 置換 */
    public String replace(String template, Map<T, String> replacements) {
        // 前提条件チェックコードは省略
        List<Token> tokens = this.tokenize(template);
        for (Token token : tokens) {
            if (token.notReplaces()) {
                continue;
            }
            replacements.keySet().stream()
                    .filter(key -> key.getReplacement().equals(token.getToken()))
                    .findFirst() // 同じキーが二度指定されている場合は事前にチェック処理を入れるしかない
                    .ifPresent(key -> token.update(replacements.get(key)));
        }
        return this.serialize(tokens);
    }
    /** 直列化 */
    public String serialize(List<Token> tokens) {
        // 前提条件チェックコードは省略
        return tokens.stream().filter(Token::notReplaces).map(Token::getToken).collect(Collectors.joining());
    }
    /** トークン化 */
    public List<Token> tokenize(String src) {
        List<Token> tokens = new ArrayList<>();
        if (src == null) {
            return tokens;
        }
        int start;
        while(0 <= (start = src.indexOf(startMarker))) { // 開始マーカーが存在する間ループ
            /* 終了位置を取得 */
            int end = src.indexOf(endMarker, start);
            if (end < 0) {
                end = src.length();
            } else {
                end += endMarker.length();
            }
            if (start != 0) { // 先頭に開始文字列がある場合は分割しない
                tokens.add(new Token(src.substring(0, start)));
            }
            final String parts = src.substring(start, end);
            tokens.add(new Token(parts, parts.endsWith(endMarker)));
            src = src.substring(end);
        }
        if (src != "") {
            tokens.add(new Token(src));
        }
        return tokens;
    }
}

試す

package sample.template;

import java.util.LinkedHashMap;
import java.util.Map;

public class ReplacerTest {

    public static void main(String[] args) {
        Replacer<PlaceholderName> replacer = new Replacer<>("#{", "}#"); // ジェネリクスから取得できるようにしてもいいかも
        Map<PlaceholderName, String> map = new LinkedHashMap<>();
        map.put(PlaceholderName.CAT_NAME, "小太郎");
        map.put(PlaceholderName.DOG_NAME, "シロ");

        System.out.println(replacer.replace("#{catName}#ちゃんと#{dogName}#ちゃんは仲良しです#{hoge", map));
    }

}

小太郎ちゃんとシロちゃんは仲良しです#{hoge

うーん、あんまりスマートじゃないなあ。