がるの健忘録

エンジニアでゲーマーで講師で占い師なおいちゃんのブログです。

「無意識に爆弾を避ける」パターン

元ネタは、ゴージャス★アイリンの、P94あたりなのですが。
ちょいとまずは引用を(漫画、面白いので是非)。

ゆ…床に爆弾がッ! ま…まさか無数の! すでに 床に無数の爆弾が仕掛けてあるのかッ!
(中略)
さ…催眠術ッ!
あいつは 敵に術をかけるのではなく
自分たちに かけていた!
それなら 無数の爆弾をふまずに生活できるッ!
あの男も 無意識に爆弾を
さけて 生活するようにしていたのか!

プログラミングとかの現場でも、割とこれに近しい事を見かけるんですよねぇ。
「この言語のこの辺の処理は地雷だから(無意識に)避ける」
「このプロジェクトのコードのこの辺は地雷だから(無意識に)避ける」
等々。
その言語やプロジェクトや環境に慣れると、なかば無意識に、それこそ「催眠術をかけた」が如く「無意識に爆弾をさけて」いくから、問題点に気付かなくなるんですよね。

んでこの辺、「新参者が入ってくる」と、途端に地雷を踏みまくります。
その時に「確かにここはマズいよねぇ」と気付くか「ダメだなぁ、のび太くんは」で片付けるか、は、割と「カイゼン分水嶺」なんじゃないか、って思うんですよねぇ。

一方、その手の「無意識に避ける爆弾」、現場に馴染んでくると結局「無意識に避けちゃうから気付かなくなってきてしまう」ので。
拾い上げるにしても割と鮮度とか大事なんじゃなかろうか、とも思っています。

……なんかもうちょっと「一撃で伝わる」ような言い方とかあるといいなぁ、とか思いつつ、思いつかないので、一端明文化しておきます。

"マジックメソッドはメソッドだから呼ばれれば動く"件について

元ネタは、 https://speakerdeck.com/twada/growing-reliable-code-phperkaigi-2022?slide=84 を見て「コンストラクタが2回動くの!?」って質問があったので。
端的には「PHPにおいてコンストラクタはマジックメソッドで、マジックメソッドはメソッドだから、callすれば動く」っていうのが身も蓋もない回答になります。

かみ砕いて。

まずPHPにおける「マジックメソッド」は、以下の通り。

https://www.php.net/manual/ja/language.oop5.magic.php

マジックメソッドは、 ある動作がオブジェクトに対して行われた場合に、 PHP のデフォルトの動作を上書きする特別なメソッドです。

"ある動作がオブジェクトに対して行われた場合"をトリガーに"PHP のデフォルトの動作を上書きする特別なメソッド"、なので、基本「メソッド」です。

んで、PHPのコンストラクタは

以下の関数名は、マジックメソッドと見なされます: __construct(), __destruct(), __call(), __callStatic(), __get(), __set(), __isset(), __unset(), __sleep(), __wakeup(), __serialize(), __unserialize(), __toString(), __invoke(), __set_state(), __clone(), __debugInfo()

とあるように、マジックメソッドです。

なので、PHPの挙動としては

インスタンスを作る
 → "インスタンスを作る"動作がオブジェクトに対して行われた場合、 PHPのデフォルトの動作を上書きする __construct() メソッドがあるなら、そいつを実行する

ってな感じになります。
なのでまぁいわゆる「コンストラクト時の動作を __construct() メソッドに書いておけば」インスタンスが生成される時に「明示的には呼んでないけど、自動でcallされる」んですね。

んで。

とはいえまぁ__construct()くんも「メソッド」なので。
「明示的にcallされた」ら、それはまぁ「呼ばれたから動く」わけなんですね。

かくしてまぁ。元ネタのDateTimeImmutableで「コンストラクタを明示的に叩くとコンストラクタが呼ばれる(ので、値が書き換えられる」ってのは、まぁ「ですよね~」くらいの挙動になります。

「はて他言語はどうなってるんだろ?」と思い、なんとかするっと目に書けるC++を(幾分思い出しつつ)書いてみたのですが。

#include <iostream>

class Hoge {
public:
    // コンストラクタ
    Hoge() {
        std::cout << "construct" << std::endl;
    }
};

int main() {
    Hoge *h = new Hoge();
    h->Hoge();

    return 0;
}

うんそもそもコンストラクタはコンストラクタであって「クラス関数ではない」んだよなぁ。
なのでまぁ

t.cpp: 関数 ‘int main()’ 内:
t.cpp:13:8: エラー: 無効な ‘Hoge::Hoge’ の使用です
h->Hoge();
^

って言われるですだよ。うん。
……それ以外の言語はど~なんだろうねぇ?*1

話を戻して。
なのでまぁ、PHPのコンストラクタは所詮「メソッド」なので、「呼べば動く」ですます。

お仕事で考える場合、この辺は「明示的に呼ぶな」で終わっていいような気がするんだよなぁ……なんていうか「そこまで配慮せにゃならんのを現場で使いたいかね?」とか、正直。
ただまぁ「思考実験として」であれば、例えばこんな感じか。

<?php
declare(strict_types=1);
error_reporting(-1);

class Hoge
{
    public function __construct()
    {
        // コンストラクタ二重callのロック機構
        static $double_call_lock = [];
        $object_id = spl_object_id($this);
        if (null !== ($double_call_lock[$object_id] ?? null)) {
            throw new \Exception('コンストラクタ明示的に呼ぶとか、おまえ、正気か?');
        }
        $double_call_lock[$object_id] = true;
    }
}

//
$obj = new Hoge();
$obj2 = new Hoge();
$obj3 = new Hoge();
//$obj->__construct();

ただ、これ(spl_object_id)も「このオブジェクトidはオブジェクトが生きている間ユニーク」なので。

//
$obj = new Hoge();
$obj = new Hoge();
$obj = new Hoge();

ってやると

[gallu@ik1-111-11111 ~]$ php t.php
int(1)
int(2)
int(1)

Fatal error: Uncaught Exception: コンストラクタ明示的に呼ぶとか、おまえ、正気か? in /home/gallu/t.php:14
Stack trace:
#0 /home/gallu/t.php(23): Hoge->__construct()
#1 {main}
thrown in /home/gallu/t.php on line 14

ってな感じになる事があるんで、あんまり十全に大丈夫でもなし。
いやまぁがっちょりとデストラクタまで書いて

<?php
declare(strict_types=1);
error_reporting(-1);

class Hoge
{
    public function __construct()
    {
        // コンストラクタ二重callのロック機構
        $object_id = spl_object_id($this);
var_dump($object_id);
        if (null !== ($this::$double_call_lock[$object_id] ?? null)) {
            throw new \Exception('コンストラクタ明示的に呼ぶとか、おまえ、正気か?');
        }
        $this::$double_call_lock[$object_id] = true;
    }

    public function __destruct()
    {
        // 「コンストラクタ二重callのロック機構」の解除
        $object_id = spl_object_id($this);
echo "unset {$object_id}\n";
        unset($this::$double_call_lock[$object_id]);
    }

private static $double_call_lock = [];
}

//
$obj = new Hoge();
$obj = new Hoge();
$obj = new Hoge();
//$obj->__construct();

ってやれば多分

[gallu@ik1-111-11111 ~]$ php t.php
int(1)
int(2)
unset 1
int(1)
unset 2
unset 1

ってな具合にはなるんだろうけど、「そこまで書く?」って話になりそうだしねぇ。

なのでまぁ「コンストラクタで値を設定、そのインスタンスの値は不変」っていうのは、PHPの場合ある程度「善意と良識」に守られている所があるんだろうなぁ、と。
ただまぁ「その辺を横紙に破りまくるような御方」は、ちょっと……って思うので。
その辺はまぁ「どーゆースタンスを持つか」次第なんじゃないかなぁ? とか思うんだけど、ど~なんだろうねぇ???

*1:コメントとかでいただければ追記いたしまする

量子的コネ取得

TRPGのお話でごんす。
システムは割と「どこでも」。

TRPGで、ある程度(以上)社会戦をやるキャラにおいて、或いはシステムにおいては「基本的に全員*1」とか。
「コネクション(知己、友人、つながり、縁故、その他)」の類いは割と必要で重要になります。

……が、一つ目には「そのシステムの世界観」と、それ以上に割とでかいのが「"マスターが認識している"そのシステムの世界観」によって、「どのコネを取るか?」ってのは、割と色々と虹色の回答が待ってます。
まぁある程度ベテランになったら「とりあえず安全かつ堅い所」とか「なんならGM相手にリアル社会戦(言いくるめ等)」なんて方法がないわけでもないのですが、そこまでいきつかない人に取っては色々とシビアで難儀な所になります。

で、おいちゃんがちょいちょいやっているのが「量子的コネ取得」。
この"量子的"は、量子将棋あたりを想起いただくとよろしいんじゃないか、と思われます。

方法。

例えば「コネが3つ取れる」ような背景やコストやポイントやCPや自由割振点なんかを支払ったとします。
キャラシには「コネ 3」とだけ書いておきます。
で、そのままセッション開始。

セッション中にコネが必要になった時に「貴方は今、コネを3つ取る事ができますが、その1つを使って**(例:警察)のコネを取得しますか?」と聞いて、もしYesなら「コネがある事にする」「そのPCのコネの1つは"警察"に固定される」という風に運用をします。
これ、全員にやってもいいし「初心者だけ」とか「上級者以外」とか、まぁその辺も割と柔軟に。

そうすると、「コネ」っていう特徴を比較的取りやすいのと、一方で「取ったのに使えなかった orz & 他のコネだったら大活躍したのに orz」っていう orz を減らす事ができます。

なんていう方法を、ちょいちょいやってまして。
ほら、いうじゃないですか「キャラメイクはフレンドリーに、セッションはデッドリーに」って*2

多分わりと「システムあんまり選ばずに使える」小技なんで、明文化してみました。

*1:シャドウランとか……まぁ把握しているのは古い版のだけど

*2:言わない

Laravelのミューテタを調べてみた

ちょっと故がありまして、ミューテタについてちょっと気になる事があったので調べてみました。
Laravelのバージョンは8で検証してますが、多分まぁどのバージョンでもさほどの差異はなかろうかなぁ、と(新しく生えたメソッドを除く)。

ミューテタについては例えば https://readouble.com/laravel/8.x/ja/eloquent-mutators.html あたりをベースに。
機能は色々あるのですが、一端、get*Attribute() とset*Attribute() を見ています。

基本機能についての基本情報は公式サイトを参考にしていただきつつ。
多分一つ「この辺は想定しているんだろうなぁ」というのが、「既存のカラムを加工合成してデータを取得する」あたりなんじゃなかろうか、と。
マニュアルでも「ユーザーのフルネームの取得」という風に書かれていますが、この辺はまぁ割と「あったら便利」って思う人も多いんじゃなかろうかと思います。
マニュアルだとこんな感じですね。

/**
 * ユーザーのフルネームの取得
 *
 * @return string
 */
public function getFullNameAttribute()
{
    return "{$this->first_name} {$this->last_name}";
}

これを書いておくと、マニュアルには書いてませんが、大体こんな感じで取得できます。

use App\Models\User;

$user = User::find(1);

$fullName = $user->full_name;

なんとなし便利だなぁ、というあたりなのですが。
ちょいと気になったのが「存在しないカラム」だけじゃなくて「存在するカラム」に対してもgetやsetを書く事ができます。
その辺で、いくつか実験してみたので、その辺を記録しておく、ってのがこの記事の目的です。

一端まず「姓と名、と後で使う文字列」用の、雑なテーブルを用意しました。

MariaDB [lara_test]> insert tests set first_name='aaa',last_name='bbb', val='val';
Query OK, 1 row affected (0.01 sec)

MariaDB [lara_test]> select * from tests;
+----+------------+-----------+-----+------------+------------+
| id | first_name | last_name | val | created_at | updated_at |
+----+------------+-----------+-----+------------+------------+
|  1 | aaa        | bbb       | val | NULL       | NULL       |
+----+------------+-----------+-----+------------+------------+
1 row in set (0.00 sec)

とりあえずまず、上述の通り「存在しないカラム名を指定して合成した値の取得」を、ざっくりと書いてみます。
「どのコードがどこに書かれているか」などは適宜推測してください(コメントとかで突っ込みが多かったら綺麗に推敲しますw)。

//
Route::get('/test', [TestController::class, 'index']);

resources/views/tests.blade.php

php artisan make:controller TestController

    public function getNameAttribute()
    {
echo "Trap getNameAttribute\n";
        return "{$this->first_name} {$this->last_name}";
    }
    public function index()
    {
        //
        $data = TestModel::find(1);
        var_dump($data->name);

まぁこんな感じで、取得ができます。

さて、ここから。

    public function getFirstNameAttribute()
    {
echo "Trap getFirstNameAttribute\n";
        return $this->first_name;
    }

これを書くとこんな風に言われます。

ErrorException: Undefined property: App\Models\Test::$first_name in file

これは、こんな風に書く必要があるようです。

    public function getFirstNameAttribute()
    {
echo "Trap getFirstNameAttribute\n";
        return $this->attributes['first_name'];
        //return $this->first_name;
    }

$attributesプロパティはマニュアルにも書いてあるので、基本的にミューテタ内では $attributes を使う、ってのがよろしい感じのようです。

    public function getAll()
    {
        return $this->attributes ;
    }

なんてのをModelに生やすと外部から「生データを一通り」取れるので、今回のような実験の時には便利か、と。

さて。
いやまぁgetも多少気になる事がないわけではないのですが、setのほうでより、ちょっと気になる事があって。

まずはこんなものを用意します。

    public function setValAttribute($v)
    {
        $this->attributes['val'] = strtoupper($v);
    }

これを書くと「valの値は全部大文字になる(事を期待している)」と思われるんですが、さてはて。
Modelにレコードを入れる時は、save()、create()、insert(単体)、insert(バルク)、Laravel8あたりからはupsert()なんてのがございます。
っつわけで、早速。

        //
        TestModel::create([
            'first_name' => 'create',
            'last_name' => 'create',
            'val' => 'aBcDe',
        ]);
        //
        $o = new TestModel();
        $o->fill([
            'first_name' => 'save',
            'last_name' => 'save',
            'val' => 'aBcDe',
        ]);
        $o->save();
        //
        TestModel::insert([
            'first_name' => 'insert',
            'last_name' => 'insert',
            'val' => 'aBcDe',
        ]);
        //
        TestModel::insert([
            [
                'first_name' => 'bulk insert',
                'last_name' => 'bulk insert',
                'val' => 'aBcDe',
            ],
            [
                'first_name' => 'bulk insert2',
                'last_name' => 'bulk insert2',
                'val' => 'aBcDe',
            ],
        ]);
        //
        TestModel::upsert([
            [
                'first_name' => 'bulk upsert',
                'last_name' => 'bulk upsert',
                'val' => 'aBcDe',
            ],
            [
                'first_name' => 'bulk upsert2',
                'last_name' => 'bulk upsert2',
                'val' => 'aBcDe',
            ],
        ], ['id']);
MariaDB [lara_test]> select * from tests;
+----+--------------+--------------+-------+---------------------+---------------------+
| id | first_name   | last_name    | val   | created_at          | updated_at          |
+----+--------------+--------------+-------+---------------------+---------------------+
|  1 | aaa          | bbb          | val   | NULL                | NULL                |
|  2 | create       | create       | ABCDE | 2022-03-25 05:16:54 | 2022-03-25 05:16:54 |
|  3 | save         | save         | ABCDE | 2022-03-25 05:16:54 | 2022-03-25 05:16:54 |
|  4 | insert       | insert       | aBcDe | NULL                | NULL                |
|  5 | bulk insert  | bulk insert  | aBcDe | NULL                | NULL                |
|  6 | bulk insert2 | bulk insert2 | aBcDe | NULL                | NULL                |
|  7 | bulk upsert  | bulk upsert  | aBcDe | 2022-03-25 05:16:54 | 2022-03-25 05:16:54 |
|  8 | bulk upsert2 | bulk upsert2 | aBcDe | 2022-03-25 05:16:54 | 2022-03-25 05:16:54 |
+----+--------------+--------------+-------+---------------------+---------------------+
8 rows in set (0.00 sec)

うんまぁ予想通りっちゃぁ予想通り。
「createとsave」はsetを通るようなのですが、insertとupsertは「setを通らない」。
多分、createとsaveは基本的に「Eloquentに所属している」のに対して、insertとupsertはおそらく「クエリビルダに所属している」んだろうなぁ、と。

あと「んじゃ、selectしてきた値をインスタンスに入れる時にはsetは通るのか?」ってのを一応確認。

    public function index()
    {
        //
        $data = TestModel::find(8);
        var_dump($data->name);
        var_dump($data->getAll());
        $data->sss('aBc');
        var_dump($data->getAll());
    }

    public function sss($v)
    {
        //$this->val = $v;
        $this->attributes['val'] = $v;
    }
Trap getNameAttribute
Trap getFirstNameAttribute
string(25) "bulk upsert2 bulk upsert2"
Trap getFirstNameAttribute
string(12) "bulk upsert2"
array(6) {
  ["id"]=>
  int(8)
  ["first_name"]=>
  string(12) "bulk upsert2"
  ["last_name"]=>
  string(12) "bulk upsert2"
  ["val"]=>
  string(5) "aBcDe"
  ["created_at"]=>
  string(19) "2022-03-25 05:16:54"
  ["updated_at"]=>
  string(19) "2022-03-25 05:16:54"
}
Trap getFirstNameAttribute
string(12) "bulk upsert2"
array(6) {
  ["id"]=>
  int(8)
  ["first_name"]=>
  string(12) "bulk upsert2"
  ["last_name"]=>
  string(12) "bulk upsert2"
  ["val"]=>
  string(3) "aBc"
  ["created_at"]=>
  string(19) "2022-03-25 05:16:54"
  ["updated_at"]=>
  string(19) "2022-03-25 05:16:54"
}

うんまぁだろうなぁ。

あとついでに。

    public function setValAttribute($v)
    {
        $this->val = 'hoge';
    }

これやると「エラーは出ないけど動かない」となります。
なんとなくなにが起きてるか? は想像できそうな感じではありますが。

さてここからおいちゃんの感想。

「存在しないカラム値のget」は、いやまぁ「それ、明示的にメソッド書いたほうがよくない?」とは思うんだけど、まぁ「ありっちゃぁアリと言えなくもない」かなぁ、と。
「存在するカラム値のget」は、そこそこ微妙。一方で「加工した値が欲しい」気持ちはわかるのですが、おいちゃんはEloquentを「ORM」と思っているので、だとすると「基本的には、生情報を返せ」って思うんですよねぇ。「加工した情報が欲しいなら、それ用のメソッドを生やせ」的な。ただまぁ「シームレスに取りたい」気持ちが全くわからないわけでもない、ので、微妙。

setについては、おいちゃんは基本「あんまりいい顔はしないなぁ」と。
一方で「便利」だったりするのはなんとなしわかるんだけど、もう一方で「隠蔽が多いと、その辺を"わかってない"人がやらかす」確率が増えて、ねぇ……………。
なのでまぁ「強弁をふるって止める」ってほどではないのですが、あんまり積極的に好印象ってわけでもないかなぁ、。

というわけで、あんまりポジティブな内容にはならんかったんですが、せっかく調べたんで、メモを兼ねて。

"The Zen of Python" と "TMTOWTDI"

いやまぁ「TMTOWTDI の字面をすっかり失念する事が多いから書いておく」とかいう超絶健忘録的な記事なのですがおいといて。

割と個人的に「対極っぽく見えるんじゃないかなぁ」と思っているのが、"The Zen of Python" と "TMTOWTDI"。

TMTOWTDI の"There's More Than One Way To Do It."をDeepLしてみるとこんな感じ。

やり方はひとつじゃない。

Google翻訳だとこんな感じ。

それを行うには1つ以上の方法があります。

まぁそのまんま。
おいちゃん的にはDeepLのほうが好みな訳語かな。

"The Zen of Python" は色々と書いてあるんだけど、TMTOWTDI と対比するのであればこの一文だろうなぁ、と。

"There should be one-- and preferably only one --obvious way to do it."

そのためには、1つ、できれば1つだけ、明白な方法があるはずです。

それを行うための明白な方法は1つ、できれば1つだけでなければなりません。

んで、以下、おいちゃんの私見

趣味なら「お好きに」なんだろうけど、お仕事だと正直おいちゃんは "The Zen of Python" 推しかなぁ。
小説とかその辺ならともかく、技術系だと、プログラムだろうが日本語だろうが基本的に「表記は揺らさないで欲しい」と思ってる。なんでかっていうと「同じような意味の事を別の文で記載している場合、一定確率で"別の事"が書かれている」から*1

もうちょっと思考を深掘りすると
・「同じ意味」と思われることが「別の記法」で書かれている
・本当に「完全に同じ」なら、「同じ記法」で書けばいいのではなかろうか?
・という事は、わざわざ「別の記法」で書いているからには「同じ記法ではなく、別の記法で書く理由」が存在する可能性が想起される
というあたりまで考えるので、結果的に「じゃぁその"別にすべき理由"はなんだろうか?」ってのを考えてしまう。

うんまぁその辺の「微妙な差異」が、手書き時の筆跡にも似た「プログラマの微妙な個性」だったりして、デバッグとかに役立つ事もあるんだけど*2
基本的には「同じ事を書くんなら同じ書き方をしてくれる」ほうが、特に「後からjoinする人間」的には楽だったりするんだよねぇコード読む時。

そんな所で個性だされても困惑するだけだし。
それは個人のプロダクトでやってよ、と、しか。

なのでまぁ、おいちゃんの好みは "The Zen of Python"。
今回取り上げた以外でも

Explicit is better than implicit.

とか

Sparse is better than dense.

とか

In the face of ambiguity, refuse the temptation to guess.

とか

If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.

とか(それ以外もよいんだがその辺言い出すと全部書き出すことになるので省略)。

まぁこの辺は色々な思考があるんだろうけど、とはいえ「自分と同じような感覚」のものを見つけると、ちょっと嬉しいよねぇ*3

*1:いやまぁ「同じ文で別のことを記載する」事がないわけじゃないんだけど

*2:古(いにしえ)の現場だと、ばぁじょんかんり、なんてもんは存在しないから、色々と推理や推測や考古学が必要になるのです……

*3:ってのを、一番初めに見た時はしみじみと感じたものです

HTMLの終了タグ(閉じタグ)のお話

ちょいと考察するタイミングがあったもんで、考察したり調べたり。
おいちゃんの基本方針は

・デザイナさんが書いてくるHTMLは「それが正義」
・おいちゃんが(主にmockupで)書く時は
 → liとかテーブル(tr th td)とかリスト(dl dt)とかは省略

くらいの感じでざっくりと書いてます。
んで、色々あってちょいと調べてたら面白いものにぶち当たったので、ちょいと書いておこうかなぁ、と。

Google HTML/CSS Style Guide
https://google.github.io/styleguide/htmlcssguide.html

ってのがあって(初めて知ったけど「まぁあるよねぇ」くらいで、驚きは無し)。

https://google.github.io/styleguide/htmlcssguide.html#Indentation

Indent by 2 spaces at a time.

へぇ「2スペなんだ」。


https://google.github.io/styleguide/htmlcssguide.html#Document_Type

Use HTML5.

うんわかる。


https://google.github.io/styleguide/htmlcssguide.html#HTML_Validity

Use valid HTML where possible.

そらそうだろうよ。


で読んでいくと……
https://google.github.io/styleguide/htmlcssguide.html#Optional_Tags

Omit optional tags (optional).
For file size optimization and scannability purposes, consider omitting optional tags. The HTML5 specification defines what tags can be omitted.

………を?
うん、例が載ってる。

<!-- Not recommended -->
<!DOCTYPE html>
<html>
  <head>
    <title>Spending money, spending bytes</title>
  </head>
  <body>
    <p>Sic.</p>
  </body>
</html>
<!-- Recommended -->
<!DOCTYPE html>
<title>Saving money, saving bytes</title>
<p>Qed.

……まぢ???
いやまぁ「雑に書く」時とか、headとbodyとか省略しているし「省略できるのは知ってる」けど、これが「推奨」なの???

「For file size optimization and scannability purposes, consider omitting optional tags.」って言われると反論がしにくいのですが(笑
いやまぁわからんでもないのですが、「削れるものは削れ」というあたりは、うん、まぁ、ピーキーで嫌いじゃないよwww

なんか過去にごくうっすら「終了タグ省略するとレンダリング(っつかその手前のパース)で時間がかかる」とかって話を聞いたような記憶がうっすらあるんだけど、どこぞの検証だと「終了タグ、削ったほうがレンダリングが早い」なんて検証もあったので。
まぁとはいえ「言うほど通信量もレンダリング速度も影響あるんかね?」とか思うので割合に「どっちでもいいんぢゃね?」くらいの感じではあるのですが。

ググるさんの規約がなんか微妙にツボったので、メモ(笑

カボチャの電子レンジ煮

なんか結構「しっとり」煮上がって気に入ったので、忘れないように。

カボチャ 150gあたり
白だし:大さじ1
・砂糖(うちはラカンカ):小さじ1
・醤油(うちはたまり):てけとう(多分、小さじ1弱くらい)
・水:50cc

これで「600wレンジ3分(*分量)」で一度ひっくり返して「もう一回、600wレンジ3分(*分量)」。

後はタッパに入れて適当に冷ませばOK。