souki-paranoiastのブログ

地方都市でプログラマーをやっている人のブログ。技術ネタ以外も少し書く。メインの言語はJava。https://paranoiastudio-japan.jimdo.com/ に所属

雑記

ChatGPT 4 有用ですね

もう話題になって結構時間が経つ気もするけど、今月頭くらいからたまに触っている。 で、過去に記載しているような小ネタというかふと気になった系のネタを書いてもらうと、自分で書いたものより精度が高い(ネットにもたくさん転がっているだろうからそれ自体にはあまり驚かないけども)。

流石にこれで大きいアプリケーションを組み立てていくとなると話は変わるけど、なかなかに面白いしびっくりする。

色々やりとりはあったけど、こんなコードを書いたら

public static <T, R> Stream<R> zippedIndex(Iterable<T> ite, BiFunction<T, Integer, R> f) {
  AtomicInteger i = new AtomicInteger();
  return StreamSupport.stream(ite.spliterator(), false)
    .map(e -> f.apply(e, i.getAndIncrement()));
}

最終的にはこうすると良いよ!って返ってきた。

public static <T, R> Stream<R> zippedIndex(Iterable<T> iterable, BiFunction<T, Integer, R> function) {
    Iterator<T> iterator = iterable.iterator();
    
    // Custom iterator that keeps track of the index
    Iterator<R> indexedIterator = new Iterator<R>() {
        private int index = 0;

        @Override
        public boolean hasNext() {
            return iterator.hasNext();
        }

        @Override
        public R next() {
            T value = iterator.next();
            R result = function.apply(value, index);
            index++;
            return result;
        }
    };

    // Convert the iterator back to a stream
    return StreamSupport.stream(
        Spliterators.spliteratorUnknownSize(indexedIterator, Spliterator.ORDERED),
        false
    );
}

無駄にmap()挟んだりAtomicIntegerで管理するくらいならまとめろって話らしい。 全く以ってその通りだと思う。

去年(! 時間経つの早いよ…)書いたJavaでSocketライブラリを用いて云々みたいなのも1分もかからずに書いてくれました。 なんならHttps接続するサンプルとかも色々書いてくれました。

本当にGoogle検索をする量が減った。 SEO対策を頑張っているのに欲しい情報のないページとかを見る時間が無くなるのよね。自然言語でも解析してくれるし。 細かいところとか平気で嘘つくところもあるけど、付き合い方を考えれば月3000円は破格の値段だよなぁと。 次ブログを書く頃にはまた情勢が変わっているのかもしれない。楽しみ。

雑に速度測った

10万回無駄な処理を実行して計算時間を測る。 zippedIndex1()が自分で書いた方。2が推奨のもの。 だいたい、1の方が35~40msで2の方が25~30msくらいだった。 1の方をintArrayで試したけどそれでも30~35msって感じ。

    public static void main(String[] args) {
        for (int i = 0; i < 1000; i++) {
            System.out.print(i);
        }
        System.out.println();
        System.out.println();

        List<String> list = IntStream.range(1, 100000)
                .mapToObj(String::valueOf)
                .toList();


        long before1 = System.nanoTime();
        List<String> list1 = zippedIndex1(list, (s, i) -> s + "__" + i)
                .toList();
        long after1 = System.nanoTime();


        long before2 = System.nanoTime();
        List<String> list2 = zippedIndex2(list, (s, i) -> s + "__" + i)
                .toList();
        long after2 = System.nanoTime();


        System.out.println(list1);
        System.out.println(list2);
        System.out.println(TimeUnit.NANOSECONDS.toMillis(after1 - before1));
        System.out.println(TimeUnit.NANOSECONDS.toMillis(after2 - before2));
    }

【JavaScript?】Webアプリの開発時にログイン情報切り替えを楽にしたい

ログインIDとパスワードの入力が面倒

面倒……じゃないですか?

だいたい管理者権限と一般権限みたいに二つくらいありますよね。ユーザによって権限を細かく設定できる場合はもっと用意する気がします。

IDだけならクエリパラメーターで設定できるようにしてあることは見かける気もするけど、パスワードまではそんな作りにしないだろうし……。

みんなはどうしているんだろう?

開発情報だしそんなに機密性無いからブラウザに保存しよう(?)

IDの文字列が大きくばらける場合は、数文字入れるだけで反応してくれるはず。

そうではないときは?もしくはそれすら面倒なら?

本題。 こういうブックマークレットか、HTMLリンクを用意しておくのはどうだろう?

ブックマーク

javascript: var x = window.open("http://localhost:8080/"); x.onload = () => { x.document.getElementById("id").value = "ID" };

HTMLリンク

<a href='javascript: var x = window.open("http://localhost:8080/"); x.onload = () => { x.document.getElementById("id").value = "ID" };'>リンク</a>

これであれば、(ブラウザなどに平文で保持されてはしまうが、)サーバ側にも情報がいかないので開発環境とはいえ他人にパスワードを見られることも無くなるし、ブックマークバーにでも表示しておけば2回クリックすればログインが終わる。

雑感

一旦これで運用してみるかな。

開発とかでなければ、GoogleログインとかのOAuth使った方が楽だよね。

ちなみに、わざわざHTMLリンクを用意しているのは今の会社とプロジェクトの関係上、ブラウザのブックマーク程度では必要なリンクを一発で表示できないのでリンク集のHTMLを用意しているからです。

このあたりも一般的にどう最適化されているんだろう。

そういえばChronium系のブックマークが今年(?)あたりからマウスの中クリックで開いてもブックマークウィンドウが閉じなくなったけど、あれが地味に凄く助かる。いちいち閉じられていたから一気に複数開けるようにHTMLファイルを用意していたけど、もう不要になりつつあるかもしれない。

蛇足

不便を感じていたのは本当だけど、今回の発端はもっと単純に「HTMLリンクを開いた後に任意のJavaScriptをコンソールとか開かずに即時実行してほしい」という考えから。

セキュリティ上いろいろと難しいのだろうけど、システムを使うのだから楽ができるようになると良いよね。

【PostgreSQL】テーブルの型(列幅と列名)を維持しつつ、レコードの一部を上書きして取得する

やりたいこと

  • select * from my_table where id = ?で取得しつつ、値を一部書き換えたい
  • select my_column1, my_column2...(以下略) from my_table where id = ?のような列挙形式ではない

ORMを使っていたり、副問い合わせのネストが深い場所で一部だけ値を上書きしたけどいちいちカラム名を列挙していくのが面倒、といったケースが偶にある。

もちろん、SQLはそんな言語じゃないとか、バグの温床だとか、速度やメモリが云々の指摘自体は全く以って正しいとは思うが、面倒なものは面倒だった。それの解消方法をメモ的に残しておく。

実装

テーブル状況

select * from item

"code","price","tax_code"
"A","100","1"
"B","200","2"

シンプルSQL

select
  new_item.*
from
  item, jsonb_populate_record(item.*, '{"code":"hoge"}'::jsonb) new_item
where
  item.code = 'A'
"code","price","tax_code"
"hoge","100","1"

当然、カラムの数は3つになるのでSQLの先頭行にinsert into itemと記述すればInsertにも使える。

SQLさえ弄れば応用も効くはず。速度は知らないが、簡単な要素であればtemporary tableも不要だろう。

select
  new_item.*
from
  item
cross join
  (select generate_series(1, 5, 1) as no) gs
cross join lateral
  (select 'code_' || gs.no as code) json_material
cross join lateral
  (select (jsonb_populate_record(item.*, to_jsonb(json_material.*))).*) new_item
where item.code = 'A'
"code","price","tax_code"
"code_1","100","1"
"code_2","100","1"
"code_3","100","1"
"code_4","100","1"
"code_5","100","1"

雑感

今触っているプロジェクトが、複数ファイルに分割されたSQLの断片があり、それを動的に文字列結合して実行…みたいなイメージで実装されている。もう少しちゃんとしているが。 left join itemみたいになっているもののレコードセットのうち、一部の値を書き換えたりしたい。だが、普通に書くとカラム名を列挙せざるを得ない。列が増えたらどうする?みたいな葛藤を解決してくれる関数だった。素晴らしい。

公式ページを読んでこの使い方が想像できず、json_to_recordの方を使って四苦八苦していた。結局型情報が必要なのかと諦めてinformation_schema.columnsを使って動的SQLを実行してplpgsqlでreturns itemみたいなことをしようかとすら考えていた。二つ目の参考記事でこれらの使い方が書いてあって助かった。

参考

www.postgresql.jp

qiita.com

JavaでSocketライブラリを使ってHTTP通信を行う

 元々はネットワークスペシャリスト試験にちょっと興味を持って、それに何も知らん人が望むには必読みたいな扱いを受けている『ネットワークはなぜつながるのか』という書籍を読み、その序盤で出てくるSocketライブラリを用いてHTTP通信する例をJavaで実行しながら理解したいという考えから触り始めた。 思ったよりも時間かかったので、せっかくなので記録として残しておく。

 HTTPS通信はSocketでやるのは結構大変らしいので純粋なHTTP通信で試す。上述の書籍もHTTPS通信に対しての言及も無いし、暗号化とかがメインで基本的な考え方は変わらないはずなので今回は無視する。

 サーバ側は適当にSpringで立てる。QueryStringとBodyをコンソールに吐きつつ返すだけのアプリを用意。 ちなみに手元の環境はspring-boot-starter.2.3.1.RELEASEだったが、大きな違いはないはず。…Spring4Shellの影響受けるやつみたいだけど稼働していないし大丈夫。

@SpringBootApplication
public class SpringTestApplication {
    public static void main(String[] args) throws Exception {
        new SpringApplicationBuilder(SpringTestApplication.class)
                .properties(Collections.singletonMap("server.port", 8000))
                .run(args);
    }
}
@RestController()
public class MyController {

    @ResponseBody
    @RequestMapping(value = "/tcpIpTest", method = {RequestMethod.GET, RequestMethod.POST})
    public String tcpIpTest(HttpServletRequest request, HttpServletResponse response) throws IOException {
        StringBuilder output = new StringBuilder();
        output.append("str *******").append(System.lineSeparator());
        output.append("QueryString").append(System.lineSeparator());
        output.append(request.getQueryString()).append(System.lineSeparator());
        output.append("Body").append(System.lineSeparator());
        request.getReader().lines().forEach(e -> output.append(e).append(System.lineSeparator()));
        output.append("end *******").append(System.lineSeparator());
        String s = output.toString();
        System.out.println(s);
        return "Return \n" + s;
    }
}

サーバが出来上がったらcurlで疎通確認。

$ curl -X POST "http://localhost:8000/tcpIpTest?xxx=a&c=123" --trace-ascii /dev/stdout -s -d '{"hoge":"fuga"}'
== Info: Expire in 0 ms for 6 (transfer 0xe948d0)
== Info: Expire in 1 ms for 1 (transfer 0xe948d0)
== Info: Expire in 3 ms for 1 (transfer 0xe948d0)
== Info: Expire in 6 ms for 1 (transfer 0xe948d0)
== Info:   Trying ::1...
== Info: TCP_NODELAY set
== Info: Expire in 149990 ms for 3 (transfer 0xe948d0)
== Info: Expire in 200 ms for 4 (transfer 0xe948d0)
== Info: Connected to localhost (::1) port 8000 (#0)
=> Send header, 169 bytes (0xa9)
0000: POST /tcpIpTest?xxx=a&c=123 HTTP/1.1
0026: Host: localhost:8000
003c: User-Agent: curl/7.64.0
0055: Accept: */*
0062: Content-Length: 15
0076: Content-Type: application/x-www-form-urlencoded
00a7:
=> Send data, 15 bytes (0xf)
0000: {"hoge":"fuga"}
== Info: upload completely sent off: 15 out of 15 bytes
<= Recv header, 15 bytes (0xf)
0000: HTTP/1.1 200
<= Recv header, 40 bytes (0x28)
0000: Content-Type: text/plain;charset=UTF-8
<= Recv header, 20 bytes (0x14)
0000: Content-Length: 83
<= Recv header, 37 bytes (0x25)
0000: Date: Sat, 09 Apr 2022 15:54:39 GMT
<= Recv header, 2 bytes (0x2)
0000:
<= Recv data, 83 bytes (0x53)
0000: Return .str *******
0015: QueryString
0022: xxx=a&c=123
002f: Body
0035: {"hoge":"fuga"}
0046: end *******
Return
str *******
QueryString
xxx=a&c=123
Body
{"hoge":"fuga"}
end *******
== Info: Connection #0 to host localhost left intact

問題ないことが確認できたので、クライアント側にあたるアプリの作成。

まず先に、Javaの標準APIで叩けるようにする。

package socket;

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;

public class SocketTest {
    public static void main(String[] args) throws Exception {
        HttpRequest request = HttpRequest.newBuilder()
                .uri(new URI("http://localhost:8000/tcpIpTest?hogehoge=xxx"))
                .POST(HttpRequest.BodyPublishers.ofString("a=1&b=ge", StandardCharsets.UTF_8))
                .build();
        HttpResponse<byte[]> response = HttpClient.newHttpClient()
                .send(request, responseInfo -> HttpResponse.BodySubscribers.ofByteArray());

        System.out.println(new String(response.body(), StandardCharsets.UTF_8));
    }
}

レスポンスはこのようになる。

Return 
str *******
QueryString
hogehoge=xxx
Body
a=1&b=ge
end *******

…とまあ、ライブラリは簡単に実装できる。普段ならこれで何も問題はないのだが、今回は勉強なのでここからが本番。

package socket;

import java.net.InetAddress;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;

public class SocketTest {
    public static void main(String[] args) throws Exception {
        String hostName = "localhost";
        int port = 8000;

        // HTTPの通信で改行が必要になるが、CRLFである必要がある。LFだけとかだと正常に動かない。
        // リンクを残すのを忘れたが結構色々なページで言及されていたが、サーバ側の対応状況によってはLFでも良いみたい。でも今回は動かないのでCRLFで。
        // https://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q1368862218
        String lineSeparator = "\r\n";

        InetAddress inetAddress = InetAddress.getByName(hostName); // ドメインからIPアドレスへの解決
        try (Socket socket = new Socket(inetAddress, port)) { // connectはコンストラクタ内部で実施。closeはtry-with-resourcesで実施。
            String messageBody = "{\"hoge\":\"fuga\"}";

            String requestLine = "POST /tcpIpTest?query=aaa HTTP/1.1";
            // ヘッダ情報は最低限の指定。Content-Typeとか諸々実際はあった方が良い
            List<String> requestHeaders = Arrays.asList(
                    "Host: %s:%s".formatted(inetAddress.getHostAddress(), port),
                    "Content-Length: " + messageBody.length()
            );


            // 完成形 ↓
            // POST /tcpIpTest?query=aaa HTTP/1.1
            // Host: 127.0.0.1:8000
            // Content-Length: 15
            //
            // {"hoge":"fuga"}

            StringBuilder requestBuilder = new StringBuilder();
            requestBuilder.append(requestLine).append(lineSeparator);
            for (String header : requestHeaders) {
                requestBuilder.append(header).append(lineSeparator);
            }
            requestBuilder.append(lineSeparator); // BODYの前に改行1つ
            requestBuilder.append(messageBody);

            socket.getOutputStream().write(requestBuilder.toString().getBytes(StandardCharsets.UTF_8));
            socket.getOutputStream().flush(); // 送信
            socket.shutdownOutput(); // 送信終了通知。HTTP1.1だとリクエストヘッダのConnection: Keep-Aliveが存在するのでそれ用のはず。
            String response = new String(socket.getInputStream().readAllBytes(), StandardCharsets.UTF_8); // レスポンス全行
            System.out.println(response);
            socket.shutdownInput(); // 受信終了通知。これはクライアント側からやるものなのか??まだ理解不足
        }
    }
}

これでひとまず動くようになる。リクエストヘッダのHostとConent-Lengthは必須らしい。無いと動かなかった。 定義まで調べた方が良いだろうけど…。 RFC 7231 — HTTP/1.1: Semantics and Content (日本語訳)

まとめていたら更に時間がかかったので今日はこの辺で。 Content-Lengthと対に(?)なるTransfer-Encoding: chunkedの実装も試していて、このあたりを利用してクライアント側からダミーデータを一定間隔でリクエストをかければ、リクエスト送信後の画面クローズとかも拾えそうだなーとかそんなのことも調べたのでまた書きたい。

Flutter2.10.0でちょっと遊ぶためにインストール

遊ぶための下準備、インストール

兎にも角にもflutter 2.10.0をインストール。

インストールしてからflutter dockerを実行すると、2つほど問題が見つかった。 Android toolchain というのと Android Studio ということで、両方Android関連ぽい。

Android Studioをinstallして再度flutter dockerを実行しても、もう1つは解決していない。

$ flutter doctor
Doctor summary (to see all details, run flutter doctor -v):
[√] Flutter (Channel stable, 2.10.0, on Microsoft Windows [Version 10.0.19043.1466], locale ja-JP)
[!] Android toolchain - develop for Android devices (Android SDK version 32.1.0-rc1)
    X cmdline-tools component is missing
      Run `path/to/sdkmanager --install "cmdline-tools;latest"`
      See https://developer.android.com/studio/command-line for more details.
    X Android license status unknown.
      Run `flutter doctor --android-licenses` to accept the SDK licenses.
      See https://flutter.dev/docs/get-started/install/windows#android-setup for more details.
[√] Chrome - develop for the web
[√] Visual Studio - develop for Windows (Visual Studio Community 2019 16.2.5)
[√] Android Studio (version 2021.1)
[√] IntelliJ IDEA Ultimate Edition (version 2018.3)
[√] IntelliJ IDEA Ultimate Edition (version 2019.3)
[√] IntelliJ IDEA Ultimate Edition (version 2021.1)
[√] Connected device (3 available)
[√] HTTP Host Availability

Run path/to/sdkmanager --install "cmdline-tools;latest" とは書いているが、sdkmanager --install "cmdline-tools;latest"を実行しても下記エラーが発生する。

bash: sdkmanager: command not found

path/to/配下じゃないので恐らく指しているものが違うのだが……どこのsdkmanagerを表現しているのだろう??pathに通っているものではないみたい。

$ where sdkmanager
D:\xxxx\cmdline-tools\bin\sdkmanager.bat

結局わからなかったのでググったらQiitaの記事がヒット。助かる。

Flutter doctor cmdline-tools componentエラー対処 - Qiita

これを参考に、インストールしたAndroid Studioを起動してcmdline-toolsをinstallする。

Android license status unknown.の箇所については書いてあるコマンドがそのまま実行できた。

flutter doctor --android-licenses

とりあえず動く環境の用意

Flutterのプロジェクトを作成する。 flutter createでプロジェクトを作るらしい。

flutter create -hの中にサンプルを作る方法が書いてあったのでまずはこれを作る。

# mysampleというフォルダでプロジェクトを作成
$ flutter create --sample=widgets.SingleChildScrollView.1 mysample

あとはIntelliJ IDEAやVS Codeなんかでこのプロジェクトを開く。

IntelliJ IDEA でChrome(Web)の指定
IntelliJ IDEA でChrome(Web)の指定

これで実行。 …少し時間を空けてChromeが立ち上がる。 (これはChromeと実際に表現されるけど、元々インストールしているChromeとは履歴情報なんかも共有していないみたいで、何なのだろう?)

めでたしめでたし……とはいかなかった。 Windows版が起動しなかった。 エラーメッセージは下記のようなもの。

ライブラリのコンピューターの種類 'x64' がターゲットのコンピューターの種類' x86' と競合しています

x86なんか入れた記憶はないが、恐らくUnityとか遊び用に入れた時に入ったのだろう。 上のflutter doctorの結果にも表示されているが、Visual Studio - develop for Windows (Visual Studio Community 2019 16.2.5)が競合しているようなので、折角だし最新のものを入れることにした。 Flutterは内部的にはC++がベースで動いているようなので対象はこんな感じにしてみた。どちらかは不要なのかもしれない。

x64モジュールのインストールをVisualStudio経由で
x64モジュールのインストールをVisualStudio経由で

最終的なflutter doctorは下記のようになった。

$ flutter doctor
Doctor summary (to see all details, run flutter doctor -v):
[√] Flutter (Channel stable, 2.10.0, on Microsoft Windows [Version 10.0.19043.1526], locale ja-JP)
[√] Android toolchain - develop for Android devices (Android SDK version 32.1.0-rc1)
[√] Chrome - develop for the web
[√] Visual Studio - develop for Windows (Visual Studio Community 2022 17.0.5)
[√] Android Studio (version 2021.1)
[√] IntelliJ IDEA Ultimate Edition (version 2018.3)
[√] IntelliJ IDEA Ultimate Edition (version 2019.3)
[√] IntelliJ IDEA Ultimate Edition (version 2021.1)
[√] Connected device (3 available)
[√] HTTP Host Availability

• No issues found!

あとは端末を再起動して、再度実行する。が、残念ながらまたエラー発生。

CMake Error: Error: generator : Visual Studio 17 2022
Does not match the generator used previously: Visual Studio 16 2019
Either remove the CMakeCache.txt file and CMakeFiles directory or choose a different binary directory.
Exception: Unable to generate build files

mysample/build/windows配下のCMakeCache.txtCMakeFilesフォルダを削除。

再度実行。やっと動いた。立ち上がるまでに結構時間かかるけど、hot reloadがあるので開発はしやすかな? Androidアプリ作ってみるか。

何しに行ったんだっけなー?な、ふらふら旅行①

暇だったので行きたかった鹿児島に行ってきた。

本当は天気とか時間とか色々都合をつけて行きたかったけど、良い条件を待つのが億劫なほどに暇だったので、つい。

 

…で、ざっくりと行きたいところと食べたいものをピックアップして、道中でどうやって回るかなーとか考えつつ鹿児島中央駅に到着。

移動手段が無いし、目的地も思ったより遠くて、到着早々にプラン変更することにw

 

本当は知覧武家屋敷と(途中で知った)知覧特攻平和会館に行こうと思ってたのに。仙厳園は近くて良さそうだったけど天気悪いので却下。

維新ふるさと館は意外と面白かった。歴史に興味ないし全部は回ってないのに1時間以上滞在していた気がする。維新に直接関係なさそうな(?)内容もあって、それも良かった。君が代に原曲とかあったのかーとか、めっちゃ洋風じゃんwみたいな知識も増えつつ。あれは歴史好きな人ならもっと居れる気がする。

 

とまあ、観光は殆どアレだけど一番の目的は豚しゃぶなので……。

旅行会社の記事か、個人のブログか忘れたが、良さそうなお店を調べていたのでそこに行くことに。結構人も並んでおり、それなりに人気みたい??

 

コース料理を注文。

人多いし、料理に対して席が狭いしでゆっくり食べれる雰囲気じゃない……(´・ω・`)

写真も一応撮ったけど、もう少し接写したかったなぁ。画質も悪いのでUPは無し!

品数は多かったし、小鉢なんかの一品一品の味が良かった。

ただ、メインの豚しゃぶがそんなに……っていう。期待値高すぎたのかな?美味しいんだけど、正直小鉢の方が好みだった。あと、〆の蕎麦は純粋に好きじゃなかった。こっちは完全に期待外れ。

もしかしたらトンカツとかの方が美味しい店だったのかもしれない。残念。次はもっと調べてから行かないと。

 

 

続いては天文館まで歩いて「天文館むじゃき」の白熊をば。

f:id:souki-paranoiast:20211109000101j:plain

かき氷の白熊。底に白豆が入っていた。画像の中央にあるお菓子は何だったのだろう?

見た目の迫力もあるし、美味しかったしで満足。白豆が入っていたのにびっくり。最後ちょっと寒かった。

ただ、顔に見えるらしいんだが、どの角度で見てもどこが顔か結局わからなかったw

 

 

天文館のアーケードが地元を彷彿とさせてくれた。地元のそれよりも観光客向けっぽいお店が多かった気がする。折角なのでお土産に甘いものを買った。

「げたんは」という郷土菓子らしい。

見た目は固めの蒸しパンのようなしっとり系のものかと思ってたけど、麩菓子に砂糖を被せたような感じだった。サクッとした食感に砂糖が加わってザクっとした感じ??

全然表現できている感はないが、お茶請けに少しなら良さそう。量は食べれん…けど結構ある。どうしようw

 

 

あとは〆に調べていたラーメン屋をと思ったのだが、この時点で18時頃だったかな?今回宿は取っていないため、ラーメン屋への往復と食事時間を…とか考えていたら結構時間が厳しかったので断念。雨と風強かったから帰りたかったのもある。傘壊れなくて良かった。

 

 

……とここまで書いておいてなんなんだが、やっぱり殆ど鹿児島を楽しめてないな??

来年リベンジ案件ですねこれは。

ラーメンが結構気になるのがあるのと、北西方面は車とかで行くと景色楽しそうなのよねぇー

 

ハーブ栽培はじめたので発芽過程とかのメモ

前置き

前から興味はあったけど面倒だったりで手を付けてなかった家庭菜園を始めてみた。 家に居る時間も増えたしね(´・ω・`)

花を見るのは好きな方だとは思うが、流石に一人で愛でても寂しいので、実用性のあるものに対象を絞った。 ずぶの素人でもとりあえず育てられるし、育っていくのを見るのは楽しいね(゚∀゚)
Amazonのレビューの発芽率はバラバラすぎてマジで参考にならん(・・;) 良い情報もあるけど。。

対象と種まき時期

  • ミント(ペパーミントかな?)
    • これだけ早い。3/23に購入しているから、多分4月の頭に種まき
  • バジル
    • 6/5に購入。すぐ種まきした記憶がある
  • ミニトマト
    • バジルと一緒に6/5に購入、種まき。ハーブじゃないけどセットだったのでついでに。
  • ローズマリー
    • 4/12に購入。種まきは6/5のはず。バジルとかと一緒に蒔いた。

ミント、バジル、ミニトマトは栽培セットを買って使用した。Amazonにも出店している「聖新陶芸 デリッシュガーデン」というところのやつ。1個800円程度でお気軽に手を出せるんだけど、種の量とか成長速度を考えると結局別途鉢植えなんかが必要になるので、なんとも言えない。始めるためのハードルは下がるので嬉しいけどね。 最終的に100均でプランターや土なんかを買って揃えた。 でもまだ種半分近く残ってるんだよなぁ。。

ミントの発芽と成長

発芽までは1週間もかからなかったはず。発芽率一番だったかも。 土に蒔いて、毎日水を適度に与えつつ乾燥を防ぐためにラップをかぶせるって方法でやってみた。

発芽してからの成長は早い。ネットでネタにされるだけはあって、ちゃんと水やりや陽の当たる場所に置くとかしていればどんどん増えていく😯 軽い気持ちで始めたから使い道に困ってる😂眺めるだけでも悪くはないけど。

バジルとミニトマトの発芽と成長

両方とも数日で発芽した。発芽率も高いし、成長も早いしでミントレベルで育てやすそう。 発芽の方法はミントと一緒で、乾燥を防ぐ方法をとってみた。 これも聖新陶芸のやつ。

ローズマリーの発芽と成長

これが一番難しかった。 種の製品説明に発芽率30%と書かれており(他は60%オーバーとか)、その時点で覚悟はしていたが一週間たっても何の反応もなくて困惑した記憶がある。 この種だけは方法を変えて2パターンほど試したが、6/9の割合で発芽したのでそちらをメモしておく。1パターン目はミントとかの方法とほぼ一緒で30%程度だったはず。

種はサカタのタネを使用した。これは栽培キットじゃない。

  1. 4~5日間ほどなるべく濡れている状態を保ち、
    • アルミホイルの上にキッチンペーパーを重ねて置き、その上に種。水。って感じ。
  2. 土の中で1~2日ほど殆ど濡れない状況に置き、
    • 発芽用のポットとか。やってはいないけど1.の環境そのままでは芽が出る感じがあんまりしなかった。
  3. やりすぎない程度に水を与える(3日~)
    • 多分3.に入る前までに根が生えているはず。で、その後に遅れて子葉(双葉)が土から目を出す感じだったので、多分これで良い。

成長は遅い。やっと小さい本葉が出てきたくらい。同時期のバジルは6枚でそれなりに大きい。

雑感

ちゃんとメモ残しておけば良かったなぁ。。あと方法もちゃんと合わせておけば良かった😂 ローズマリーの1パターン目は栽培キットを使っていないから対照としては正しくないし、2パターン目も日数が若干怪しいし。 まあ、これくらい適当でも発芽はするし、プランターの数が無ければどうせ間引かないといけないからどうでもいい気はするが。

ついでに聖新陶芸の製品レビューもしておくと、少なくともトマトの栽培キットの種はサカタのタネだったから、私よりもエンジョイ勢向けの製品なのかもしれない。種代だけなら200円程度のはずだし。トマトをあの栽培キットで育てるのは多分大変なはず。ミントやバジルのように「すぐに育ってすぐにちょっと使える」みたいな品種だとキットの規模感にも合っていて丁度良さそうだと思った。

どうでもいいけど、発芽後の葉の呼称は「子葉」ってことを初めて知った。「双葉」で習った記憶があるけど、2007年の時点で子葉らしい。 【理科】子葉と双葉、どっちが正しいの?|ベネッセ教育情報サイト