プログラミング原人の進化ログ

プログラミング原人の進化論

オレ プログラミング ベンキョウ スル。マナンダ コト カク。

Atomのファイル検索を便利にしてくれるパッケージ

やること

Atomadvanced-open-fileをインストールを導入します。

環境

動機

fuzzy検索したい。キーボードから手を離したくない。

方法

インストール

ターミナルで以下のコマンドを叩きます。

$ apm install advanced-open-file

再起動したら有効化されます。

使い方

デフォルトでcmd-alt-Oで開きます。押しにくいので次のcmd-x cmd-fを割り当てました。 キーバインドを設定するには~/.atom/keymap.csonに次を追加します。

'.platform-darwin atom-workspace':
  'cmd-x cmd-f': 'advanced-open-file:toggle'

開いたらファイル名を入力し、候補を選択するだけです。ctrl-n, ctrl-pで移動、Returnで選択。

設定

そのままで使えますが、個人的に以下のように設定しています。 設定はadvanced-open-fileのSettingsから各項目にチェックを入れます。

  • Create directories
    存在しないディレクトリを指定した場合、そのディレクトリを作成する。
  • Create files instantly
    存在しないファイルを指定した場合、そのファイルを作成する。
  • Use fuzzy matching for matching filenames
    fuzzy検索(あいまい検索)になる。

まとめ

だいぶ楽になりました。

参考

GitHub - Osmose/advanced-open-file: Open files and folders in Atom easily.

Atomで1単語削除のコマンドを作る

Atom上でカーソル位置にある単語をまるまる削除するコマンドが欲しくて、探してみたがなかったので自作します。
単語の一部を削除するコマンドはあったのですが、個人的には全体を削除する方が好きなので。

環境

方法

単語の前方を削除するコマンドeditor:delete-to-beginning-of-wordと単語の後方を削除するコマンドeditor:delete-to-end-of-wordを組み合わせて新しいコマンドcustom:cut-wordを定義します。~/.atom/init.coffeeに以下を追加。

atom.commands.add 'atom-text-editor', 'custom:cut-word', ->
  view = atom.views.getView atom.workspace.getActiveTextEditor()
  atom.commands.dispatch view, 'editor:delete-to-beginning-of-word'
  atom.commands.dispatch view, 'editor:delete-to-end-of-word'

init.coffeeAtom起動時に評価されるらしいので、一度再起動します。

上で追加したコマンドにキーを割り当てます。~/.atom/keymap.csonに以下を追加。

'atom-text-editor':
  'ctrl-w': 'custom:cut-word'

これでC-wで1単語を削除できるようになりました。

余談

init.coffeeAtomAPIにアクセスして色々いじれるようです。

コマンドは⌘-, > keybindingsで調べられます。

参考

1つのキーバインドで、同時に複数のコマンドを実行する - Qiita

pythonでOpenWeatherMapのweatherAPIを叩く

環境

Mac OS X 10.14.5 python 3.7.1

OpenWeatherMapに登録

ここにアクセスして"Weather APIs for developers"の下の"Try free APIs"ボタンを押下します。
すると次のようなページが表示されるので、

f:id:programgenjin:20190709135103p:plain

sign up から必要な情報を入力して"create account"ボタンを押下します。

API keys からKEYを確認します。

f:id:programgenjin:20190709140147p:plain

このKEYはあとで使います。

使ってみる

京都の現在の気象情報をJSON形式で取得。

import requests
import json

city = "Kyoto"
KEY = "xxxxx"  # 先ほど確認したKEYを指定
api = "http://api.openweathermap.org/data/2.5/weather?units=metric&q={city}&APPID={key}"
url = api.format(city=city, key=KEY)
response = requests.get(url)
data = response.json()  # dataをJSONで取得
print(json.dumps(data, indent=4))  # JSONを整形

実行結果

{
    "coord": {
        "lon": 135.76,
        "lat": 35.02
    },
    "weather": [
        {
            "id": 803,
            "main": "Clouds",
            "description": "broken clouds",
            "icon": "04d"
        }
    ],
    "base": "stations",
    "main": {
        "temp": 24.78,
        "pressure": 1006,
        "humidity": 78,
        "temp_min": 24,
        "temp_max": 25.56
    },
    "visibility": 10000,
    "wind": {
        "speed": 2.1,
        "deg": 90
    },
    "clouds": {
        "all": 75
    },
    "dt": 1562891024,
    "sys": {
        "type": 1,
        "id": 8032,
        "message": 0.0088,
        "country": "JP",
        "sunrise": 1562874703,
        "sunset": 1562926370
    },
    "timezone": 32400,
    "id": 1857910,
    "name": "Kyoto",
    "cod": 200
}

国を含めて指定する場合はTokyo, Japanのようにします。他にも色々な指定方法があるようです。

まとめ

思ったよりも簡単にできました。天気予報のアプリを作るときにでも役立ちそう。

参考

pythonを使ってOpenWeatherMapから天気情報を取得 - Qiita

【Python】リストの要素を独立に変化させたときの全ての組み合わせを求める

何をするのか

わかりにくいタイトルですが、例えばリスト[x1, x2]を考えたときにx1, x2がそれぞれ0, 1, 2のいずれかの値を取るとします。この時、組み合わせの総数は3^2 = 9通りです。
こういった場合に、全ての組み合わせを列挙する方法を考えます。

方針

わかりやすくするためにリストの要素は整数とします。

f:id:programgenjin:20190707115936p:plain

上の例でいうと、取り得る値のリスト[0, 1, 2]を要素の個数分(ここでは2つ)用意して、そこから1つづつ値を取ってくることにします。
これを実現するには直積を利用します。
直積というのは、いくつかの集合から1つづつ元をとってできる全ての組を元とする集合のことです。

実装

pythonで直積を作るにはitertoolsを使うと便利です。itertools.product()は、引数に与えたリストの直積をとってくれます。

import itertools

N = 2  # リストの要素数
lim_lst = [range(3) for _ in range(N)]  # 取り得る値のリストをN個用意
list(itertools.product(*lim_lst))

実行結果

[(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]

lim_lstを展開して渡していることに注意。

まとめ

直積を用いればいいと気づいてしまえば簡単でしたが、特にリストの要素数が動的に決まる場合はそうでなければ苦労しそうです。

参考

itertoolsによる順列、組み合わせ、直積のお勉強 - Qiita

MySQLのユーザ・データベースの操作コマンドをまとめておく

何度やっても忘れるので、メモ。よく使うものをまとめておきます。
適宜、追加していきます。

環境

ユーザ

最初にサーバを起動してログインしておきます。

ユーザ名とホスト名の一覧を表示

mysql> select user, host from mysql.user;

ユーザを作成

mysql> create user 'ユーザ名'@'ホスト名' identified by 'パスワード';

ユーザを削除

mysql> drop user 'ユーザ名'@'ホスト名';

データベース

データベース一覧

mysql> show databases;

データベースを作成

mysql> create database データベース名;

データベース削除

mysql> drop database データベース名;

権限の付与

特定のユーザに、指定したデータベースを操作する権限を与える

grant all on データベース名.* to 'ユーザ名'@'ホスト名';

参考

データベースを削除する(DROP DATABASE文) | MySQLの使い方

WordPress推奨構成とMySQLデータベースの作成 - WordPressの使い方

空のndarrayに列ベクトルや多次元配列を結合させる

最初に空の配列を作っておいて、後からそこに色々くっつけたい時があります。行ベクトルだと簡単に結合させられますが、このたび列ベクトルや多次元配列を結合させる方法を発見したのでメモ。

行(横)方向に連結する場合、hstackを使います。結合させたいものと同じ行数と同じ個数の空の配列を持った配列をあらかじめ作っておきます。

例:

a = np.array([[] for _ in range(3)])
b = np.array([1, 2, 3]).reshape(-1, 1)
c = np.hstack((a, b))
c

実行結果:

array([[1.],
       [2.],
       [3.]])

a = np.array([[] for _ in range(3)]).reshape(-1, 1)とするとうまくいかないことに注意。こっちの方が直感的には正しそうですが、理由はよくわかりません。
多次元配列も同様。

横方向はこんな感じでいけますが、縦方向に繋げる方法はわからず。横方向に結合させてから最後に転置するか、最初に0ベクトルを作ってvstackで縦方向に連結して最後に1行目を削除するか...。あんまりスマートではないかな。
横に連結する方法ももっと良いやり方があるかもしれませんが。

SpringBootでLombokを使えるようにする

SpringBootで開発しているときにlombokを使いたいと思って、SpringToolSuitにlombokをインストールしたものの、importできなかった時の解決メモです。
lombokを利用するとアノテーションをつけるだけでsetterやgetterを勝手に実装してくれるらしいです。他にも色々な機能があるみたい。

やること

SpringBootでlombokを使えるようにする。STSにインストールします。
具体的な使い方については扱いません。

環境

  • Mac OS X 10.14.5
  • SpringToolSuit4
  • SpringBoot 2.1.6
  • Gradle 5.4.1
  • Lombok 1.18.8

インストール手順

ここからjarファイルをダウンロードしてきて、java -jar lombok.jarインストーラを起動。

f:id:programgenjin:20190627142003p:plain

インストール先のIDESTSが見当たらないので、specify location からSTSを選択。僕の場合は
Applications > SpringToolSuite4 > Contents > Eclipse > SpringToolSuite4.ini
STSを選択できました。

それから Install / Update をクリックしてSTSを再起動したら使えるようになるはず。

と思ったら、import lombok.DataとしたらThe import lombok cannot be resolvedとエラーが出てきました。

importできない問題の解決法

build.gradleのdependenciesに

dependencies {   
    compileOnly 'org.projectlombok:lombok:1.18.8'
    annotationProcessor 'org.projectlombok:lombok:1.18.8'
}

の2行を加え、プロジェクトを右クリックしてGradle > Refresh Gradle Project とします。これで解決しました。

参考

Gradle

Emacsでhtmlのリアルタイムプレビュー

Emacsとブラウザを使って、htmlファイルをリアルタイムでプレビューしながら編集する方法です。

プレビューを開始

次の手順でリアルタイムプレビューを行う。

  1. サーバを起動:M-x httpd-start
  2. プレビューしたいバッファを開いてimpatient-modeをONにする:M-x impatient-mode
  3. ブラウザでhttp://localhost/8080/impにアクセス
  4. impatient-modeがONになっているファイルの一覧が出てくるので、表示したいものを選択

CSSを反映させておきたいときはCSSファイを編集しているバッファでも忘れずにimpatient-modeをONにしておきます。

プレビューを終了

終了するには次の手順を踏む。

  1. impatient-modeがONになっているバッファでimpatient-modeをOFFにする:M-x impatient-mode
  2. サーバを停止:M-x httpd-stop

MySQLのrootパスワードの設定・ユーザ登録

初期設定で、rootパスワードを設定するときにつまったので、メモを残しておきます。 結論から言うとMySQLのバージョンが原因でした。
MySQLをインストールしてから、rootのパスワード設定および新しくユーザを登録するところまでやります。

環境

  • Mac OSX 10.14.5
  • MySQL 8.0.16

インストール

Homebrewを使います。

> brew install mysql

MySQLの起動/停止

何かする時には起動します。個人的に忘れてエラーが出たりしたのでメモ。

  • 起動:
> mysql.server start
  • 停止:
> mysql.server stop

初期設定:rootパスワードの登録

初期状態ではパスワードが設定されていないので、rootユーザとしてログインして登録します。最初にrootのパスワードを登録して、次回からパスワード付きでログインすることになります。

まずはMySQLサーバを起動します。

> mysql.server start

rootでログインします。

> mysql -u root

rootのパスワードを設定します。ここで引っかかりました。次のようなコマンドを叩くと、

mysql> update mysql.user set password=password('パスワード') where user = 'root';

こんなエラーを吐かれました。

ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '('rootpass') where user = 'root'' at line 1

調べてみると、今回使用したMySQLのバージョン(8.0.16)では別の書き方に変わっているとのことです。
こちらを参考にもう一回rootパスワードの設定。

mysql> alter user 'root'@'localhost' identified by 'パスワード';
Query OK, 0 rows affected (0.01 sec)

今度はうまくいったようです。
一旦ログアウトして、

mysql> exit;

今度はrootにパスワードつきでログインしてみます。

> mysql -u root -p

パスワードを聞かれるので、入力するとログインできました。

ユーザの登録

rootの設定ができたので、今度はユーザを作成してみます。 こちらを参考にしました。

mysql> create user 'ユーザ名'@'ホスト名' identified by 'パスワード';
Query OK, 0 rows affected (0.07 sec)

一旦ログアウトします。

mysql> exit;

先ほど作成したユーザでログインしてみます。

> mysql -u ユーザ名 -h ホスト名 -p

パスワードを聞かれるので、入力するとログインできます。

まとめ

古いバージョンの情報が多く転がってるので、ちょっと苦戦しました。バージョンの確認は大事。

Javaのtime APIでよく使いそうなクラスとメソッド一覧

今までサボってきた時間情報の取り扱いを勉強したが、忘れそうなのでまとめておきます。自分用のチートシート的な位置付けです。

記事の内容

Dateクラスとかよりも新しい方法が登場しているらしいのでそちらをまとめておきます。
具体的には、java.timeパッケージに属する次のクラスを主に扱います。

  • Instant
  • ZonedDateTime
  • LocalDateTime
  • LocalDate
  • Duration
  • Period

主なクラスの使い道

クラス 説明
Instant 1970年元旦0時ちょうど(Epoch)からの経過ミリ秒を保持する
ZonedDateTime タイムゾーン付き日時をミリ秒まで保持する
LocalDateTime タイムゾーンなし日時をミリ秒まで保持する
LocalDate タイムゾーンなし日時を日単位で保持する
Duration 2つ日時(時刻など短い期間)の間隔を保持する
Period 2つの日時(日付など長めの期間)の間隔を保持する

インスタンを作成するにはnewするのではなくnow(), of(), parse()メソッドを用います。

Duration, Periodのインスタンス生成にはof〇〇()(ofDays, ofMonths()など), between()メソッドを用います。

Period p1 = Period.ofDays(3);

LocalDate day1 = LocalDate.of(2019, 6, 8);
LocalDate day2 = LocalDate.of(2019, 6, 10);

Period p2 = Period.between(day1, day2);

共通のメソッド

とりあえず主要なメソッドの一覧。使い方は後ほど。

メソッド 説明
now() 現在の時刻を保持するインスタンスを生成する
of() 指定した時刻を保持するインスタンを生成する
parse() 指定したフォーマットの文字列を変換した時刻を保持するインスタンスを作成する
format() 時刻を保持するインスタンスを指定したフォーマットで文字列に変換する
get〇〇() 月、日などの個別の情報を取得する
isAfter() 引数の時刻より後かどうか判定する
isBefore() 引数の時刻より前かどうか判定する
plus〇〇() 指定した分だけ進んだ時刻を返す
minus〇〇() 指定した分だけ戻った時刻を返す
plus() 引数に与えた期間だけ進んだ時刻を返す
minus() 引数に与えた期間だけ戻った時刻を返す

タイムゾーンの指定方法

ZondIdオブジェクトを使います。
ZoneIdオブジェクトは次のようにタイムゾーンを引数にして作成します。

ZoneId.of("Asia/Tokyo")

タイムゾーンの一覧は(IANA)[]で確認できます。

get〇〇, plus〇〇, minus〇〇メソッド

〇〇で時間単位を指定します。具体的には次の通り。

getYear(), getMonth(), getDayOfMonath(), getHour(), getMinute(), getSecond(), getNano()

plusYears(), plusMonths, plusDays(), plusHours(), plusMinutes(), plusSeconds(), plusNanos()
(minusはplusと同じ)

各メソッドの使い方

now

クラス名 インスタンス名 = クラス名.now()の形。

Instant i1 = Instant.now();

of

必要なだけの時刻情報を与えます。
下の例は2019年6月8日15時0分0秒0ミリ秒、東京での時刻を保持します。

ZonedDateTime z1 = ZonedDateTime.of(2019, 6, 8, 15, 0, 0, 0, ZoneId.of("Asia/Tokyo"));
LocalDate l1 = LocalDate.of(2019, 6, 8);

parse, format

時刻のオブジェクトを文字列に変換するにはjava.time.format.DateTimeFormatterを使います。 ofPatternメソッドでフォーマットを指定します。

DateTimeFormatter dtf = DateTimeFormatter.ofPattern("西暦yyyy年M月d日");
LocalDate day = LocalDate.parse("西暦2019年6月8日", dtf);
String strDay = day.format(dtf);

get〇〇

返り値はint型みたいです。

LocalDate date = LocalDate.of(2019, 6, 8);
int y = date.getYear();

isAfter, isBefore

真偽値が返ります。

LocalDate day1 = LocalDate.of(2019, 5, 8);
LocalDate day2 = LocalDate.of(2019, 6, 8);
System.out.println(day1.isAfter(day2));  // false
System.out.println(day1.isBefore(day2));   // true

plus〇〇, minus〇〇

LocalDate day1 = LocalDate.of(2019, 5, 8);
LocalDate day2 = day1.plusDays(3);  
LocalDate day3 = day1.minusYears(1);  

plus, minus

LocalDateTime time1 = LocalDateTime.of(2019, 6, 8, 15, 0, 0, 0);
Duration d = Duration.ofHours(3);
Period p = Period.ofDays(3);
LocalDateTime time2 = time1.plus(d);
LocalDateTime time3 = time1.minus(p);

参考

中山清喬(2014)「スッキリわかるJava入門 実践編」株式会社インプレス

Emacsでelpyを使ったPython開発環境を設定した

elpyを入れたので、インストール方法と設定を書いておきます。ちょっとつまったのででそれの解決法のメモを兼ねてます。

環境

Mac OS X 10.14.5

Emacs 26.1

elpyとは?

Emacs用のPython開発環境です。これ一つで必要なパッケージが一通り揃います。

elpyを使えるようにする

普段package-installを使っているのですが、なぜかうまくいかなかったので、M-x package-list-packagesでインストールしました。

使えるようにするにはinit.elなどに

(package-initialize)
(elpy-enable)

と書いておきます。pythonファイルを開けば勝手に使えるようになります。

必要に応じて、各種ツールをインストールしておきます。

設定

pythonを使うために色々設定したので載せておきます。現在どのツールを使っているかはM-x elpy-configでチェックできます。 足りないパッケージがあれば教えてくれるので、それに従って必要なものをインストールします。f:id:programgenjin:20190607210240p:plain

marmaladeでインストールしたら自動フォーマッタ(yapfとかblackとか)をelpaが認識してくれなかったので、elpaのものを入れ直したらうまく行きました。

以下は個人的な設定です。

(add-hook 'elpy-mode-hook
          '(lambda ()
             ;; keybind
             (define-key elpy-mode-map (kbd "C-,") 'elpy-nav-indent-shift-left)  ;; 左にインデント1つ分移動
             (define-key elpy-mode-map (kbd "C-.") 'elpy-nav-indent-shift-right)  ;; 右にインデント1つ分移動
             (define-key elpy-mode-map (kbd "s-c s-s") 'elpy-shell-switch-to-shell)  ;; インタラクティブシェルを開く
             ;; format code automatically
             (add-hook 'elpy-mode-hook 'py-yapf-enable-on-save)))
;; use flycheck
(when (require 'flyckeck nil t)
  (remove-hook 'elpy-modules 'elpy-module-flymake)
  (add-hook 'elpy-mode-hook 'flycheck-mode))

標準ではシンタックスチェックにflymakeを使っているようなのでflycheckを使うようにしています。
yapfを使って、バッファを保存した時に自動整形を行うようにしています。

まだ導入したばかりなので、使っていくうちにやりたいことも増えて設定も増えてくるかもしれませんが、とりあえずこんなものかと。現時点でデバッガを使いこなせてないのできちんと使えるようになりたい。

参考

Elpy — Elpy 1.30.0 documentation

個人的な Python 用の Emacs の設定 | org-技術

【Python】suptitleでグラフ全体と個別のグラフに別々にタイトルを与える

matplotlibで1つのfigureに複数のグラフを配置する時に、個別のグラフのタイトルだけでなく全体のタイトルをつけることができたら便利だなと思ったのでやり方をメモしておきます。

タイトルの付け方

全体のタイトルを設定するにはplt.suptitle('タイトル')を使います。

f:id:programgenjin:20190605174917p:plain

import numpy as np
import matplotlib.pyplot as plt

fig, axes = plt.subplots(1, 2, figsize=(10, 5))

x = np.linspace(0, 2 * np.pi, 100)

ax0 = axes[0]
ax0.plot(x, np.sin(x))
ax0.set_title('sin')

ax1 = axes[1]
ax1.plot(x, np.cos(x))
ax1.set_title('cos')

# 全体のタイトル
plt.suptitle('sine and cosine')

plt.show()

タイトル位置の調整

グラフによっては、suptitleで設定した全体のタイトルがグラフのタイトルと重なってしまう場合があります。
そういう時はsuptitleの引数yによって調整します。

例えばタイトルの高さを1.05としたいときは

plt.suptitle('タイトル', y=1.05)

yの値はデフォルトで0.98らしいです。
y=1でグラフ全体の高さ?

参考

グラフの配置 - Python Project

matplotlib.pyplot.suptitle — Matplotlib 3.1.0 documentation

【Java】スレッドの基本

Javaのスレッドについて基本的なことをまとめておきます。

スレッド

Javaでは複数の処理を同時に並行して行うことができます。それぞれの処理の分岐のことをスレッドと言います。

スレッドの作り方

処理を分岐させてスレッドを作る方法は2つあります。

スレッドの作り方1

1つ目は、クラスライブラリのThreadクラスを継承したクラスを作り、そこに別スレッドで行いたい処理を書く方法です。 Threadクラスのサブクラスでrun()メソッドを定義すると、その内容がスレッドを新しく起動した時に行われる処理となります。

class クラス名 extends Thread {
  public void run() {
  // 新しく起動したスレッドで行いたい処理
  }
  ...
}

このクラスのインスタンスを作成し、start()メソッドを適用することによって、新しくスレッドが起動します。

例を見てみます。

Hello1.java

class Hello1 extends Thread {
  private String name;

  public Hello1(String name) {
    this.name = name;
  }
  
  public void run() {
    for(int i = 0; i < 3; i++) {
      System.out.println("Hello, " + this.name);
    }
  }
}

Example1.java

class Example1 {
  public static void main(String[] args) {
    Hello1 hello = new Hello1("World");

    hello.start();  // 新しいスレッド起動

    for (int i = 0; i < 3; i++) {
      System.out.println("Main");
    }
  }
}

この2つを同じディレクトリに保存し、実行すると次のようになりました。

Main
Main
Main
Hello, World
Hello, World
Hello, World

なんか、あんまり並列処理している感じがないですが...。実行環境によって出力される順番は変わると思います。

スレッドの作り方2

2つ目は、クラスライブラリのRunnableインターフェースを実装するクラスを作る方法です。 この場合はThreadクラスのインスタンスを作成してそれに対してstart()メソッドを適用することになります。

例を見てみます。

Hello2.java

class Hello2 implements Runnable {
  private String name;

  public Hello2(String name) {
    this.name = name;
  }
  
  public void run() {
    for(int i = 0; i < 3; i++) {
      System.out.println("Hello, " + this.name);
    }
  }
}

Example2.java

class Example2 {
  public static void main(String[] args) {
    Hello2 hello = new Hello2("World");
    Thread th = new Thread(hello);

    th.start();  // 新しいスレッド起動

    for (int i = 0; i < 3; i++) {
      System.out.println("Main");
    }
  }
}

実行結果:

Main
Main
Main
Hello, World
Hello, World
Hello, World

スレッドを一時停止する

sleep()メソッドを使います。オブジェクト.sleep(x)とすることで処理がxミリ秒だけ停止します。

次の例はHello1.javaExample1.javaのうち、Hello1.javaのrun()メソッドだけを書き換えたものです。エラー処理を書かないとコンパイルする時に怒られます。

Hello3.java

class Hello3 extends Thread {
  private String name;

  public Hello3(String name) {
    this.name = name;
  }
  
  public void run() {
    for(int i = 0; i < 3; i++) {
      try {
        this.sleep(1000);
        System.out.println("Hello, " + this.name);
      }
      catch(InterruptedException e) {}
    }
  }
}

Example3.java

class Example3 {
  public static void main(String[] args) {
    Hello3 hello = new Hello3("World");

    hello.start();  // 新しいスレッド起動

    for (int i = 0; i < 3; i++) {
      System.out.println("Main");
    }
  }
}

実行結果:

Main
Main
Main
Hello, World
Hello, World
Hello, World

それぞれのHello, Worldが表示されるまでに1000ミリ秒=1秒が経過します。

他のスレッドが終了するまで待機する

join()メソッドを使います。あるオブジェクトに対してjoinメソッドを用いると、そのオブジェクトに関連づけられたスレッドが終了するまで待機し、それから以降の処理を行います。

Example1を改造した例です。

Hello4.java

class Hello4 extends Thread {
  private String name;

  public Hello4(String name) {
    this.name = name;
  }
  
  public void run() {
    for(int i = 0; i < 3; i++) {
      System.out.println("Hello, " + this.name);
    }
  }
}

Example4.java

class Example4 {
  public static void main(String[] args) {
    Hello4 hello = new Hello4("World");

    hello.start();  // 新しいスレッド起動

    // hello に関連づけられたスレッドが終了するまで待機
    try {
      hello.join();
    }
    catch (InterruptedException e) {}

    for (int i = 0; i < 3; i++) {
      System.out.println("Main");
    }
  }
}

実行結果:

Hello, World
Hello, World
Hello, World
Main
Main
Main

スレッドを同期させる

複数のスレッドが同じあるメソッドを呼び出す時、メソッドの呼び出しを同時に行わないようにすることを同期と言います。各スレッドは順番にそのメソッドの呼び出しを行うことになります。
そのようなメソッドを作りたいときは、メソッドの宣言時にsynchronizedとつけます。

アクセス指定子 synchronized 戻り値の型 メソッド名() {
  …
}

参考

高橋麻奈(2019) 「やさしいJavaSBクリエイティブ株式会社

Javaのインターフェース(確認用)

Javaのインターフェースを勉強したので、そのメモです。
書式を確認するためのものです。

インターフェース

インターフェースは実体化することはできない。

インターフェースの宣言

次のように宣言する。

interface インターフェース名 {
  ...
  型 フィールド名 = 初期値;
  返り値の型 メソッド名(引数);
  ...
}

気をつけるべきことは、

  • フィールドは初期化する
  • メソッドは宣言するだけ(処理は書かない)
  • フィールドやメソッドを宣言するときには修飾子はつけない。

修飾子をつけないが、フィールドにはpublic static final、メソッドにはabstractをつけたのと同じになる。
つまり、フィールドは定数、メソッドは抽象メソッドとなる。

インターフェースの実装

インターフェースで宣言したメソッドの処理は、クラスで実装することになる。

class クラス名 implements インターフェース名 {
  ...
  // インターフェースで宣言したメソッドを定義
  ...
}

複数のインターフェースの実装

Javaでは多重継承(複数のスーパークラスを持つこと)はできないが、1つのクラスが複数のインターフェースを実装することはできる。

class クラス名 implements インターフェース1, インターフェース2, ... {
  ...
  // インターフェース1で宣言したメソッドを定義
  ...
  // インターフェース2で宣言したメソッドを定義
  ...
}

インターフェースの拡張

クラスの継承のようなもの。拡張の関係における親をスーパーインターフェース、子をサブインターフェースという。
複数のスーパーインターフェースを持つことができる。

interface サブインターフェース名 extends スーパーインターフェース1, スーパーインターフェース2, ... {
  ...
}

サブインターフェースを実装するクラスはスーパーインターフェースをも実装しなければならないことに注意。

参考

高橋麻奈(2019) 「やさしいJavaSBクリエイティブ株式会社

Emacs上のシェルでコマンドを再利用する方法

ターミナルのC-pでと同じようにEmacs上で立ち上げたシェルでもコマンド履歴を遡れることを発見しました。Emacs使いの方々の間ではすでに常識かもしれませんが...。

シェルモード

shell, eshell, multi-term で確認しました。
挙動は若干違いますが、基本的には全て同じやり方でできます。

コマンドの再利用

一つ前のコマンド:

M-p

次のコマンド:

M-n

まとめ

今まで知らなかったので基本的にターミナルでshellを使っていたのですが、これからはEmacs上でshellを使う頻度が増えそうです。shell-popと併用するとかなりターミナルに近い感覚で使えました。補完が強力でコピー・ペーストが簡単なぶん、こっちの方が使いやすい気がします。

あと、例えばeshellだと128個のコマンドしか記録していないようなので、(今の所困ってはいないが)ログ数も変えたくなるかも。

参考

Shell Ring (GNU Emacs Manual (Japanese Translation))