無駄と文化

実用的ブログ

オートマットのプレステージが好きだ

「オートマット」という製品がある。
プラスチックでできた板で、地面に敷き詰めることで簡易舗装ができる製品だ。

公式サイト ( https://www.web-prestige.com/automat/ ) より引用

私はこのオートマットを販売しているプレステージの公式 X (旧: Twitter) が大好きだ。

 

簡易舗装材 オートマットのプレステージ

そのアカウントがこちら。「簡易舗装材 オートマットのプレステージ」だ。
(以降、「オートマットのプレステージ」と記載)

twitter.com

この公式 X (旧: Twitter) が最高。とても良い。

オートマットのプレステージは毎日1ポストを淡々と投稿している。
投稿文は毎回固定で、1枚の画像を添える形式になっている。
文章は固定だが画像はバリエーション豊かで使い回しが少ない。オートマットの使用例をたくさん見せてくれる。

 

何が良いのか・なぜ好きか

ここから本文。

オートマットのプレステージは私にとって X (旧: Twitter) における "癒し" になっている。
投稿文に変化は無く、投稿ペースも一定。ときどき目に入って「いつもの文章」と「いろいろな画像」で楽しませてくれる。

オートマットのプレステージに "驚き" は無い。不安にさせられたり、期待を裏切られるようなことも無い。私をあわてさせるような要素はどこにも無い。
これまでも無かったし、きっとこれから先も無いだろう。

それくらい安心して見てられる。

 

まさに実家のような安心感。

 

現代のインターネットは驚き・意外性に満ちている。
センセーショナルなものほど拡散され、喜び・怒り・悲しみといった感情がひと目に晒されている。

でも、そんな "味付けの濃いインターネット" ばかり摂取していたら疲れませんか?
たまには静かな部屋でゆっくりとお茶でも飲みませんか?
「スタバの新作」ではなく「実家で出てくるいつものお茶」でこそホッと安心してくつろげる、そう思いませんか?

 

そこでオートマットのプレステージ。

 

あー、心のわだかまりがほどけてゆく。

 

投稿文を鑑賞しよう

雨が降ると地面がぬかるんで困っている方へ。ご自身で簡易舗装ができるオートマットはいかがでしょうか。
オートマット1枚のサイズは縦横50cm、厚み2cm、重さ1.8kg、耐荷重4tの硬質マットです。
オートマットは自動車バンパーのリサイクル製品です。日本製。

138文字。
コンパクトでさっぱりとした文章。でもしっかりとオートマットの良さを伝えていて爽やかな気持ちになる。

無駄がなくスッと染み込んでくる。まるで芭蕉の俳句のようだ。

 

まとめ

インターネットで疲弊しているみなさん。オートマットのプレステージをフォローしましょう。

 

 

私からは以上です。

 

蛇足

Q: これは記事広告ですか?

A: いいえ、ラブレターです

Rust における関数とメソッドの使い分け

Rust ネタです。

Rust では構造体に対してメソッドを定義できます。
公式のドキュメント では Rectangle 構造体に対して面積を求める area メソッドを定義する例が紹介されています。

struct Rectangle {
    width: f64,
    height: f64,
}

impl Rectangle {
    /// 四角形の面積を求める
    fn area(&self) -> f64 {
        self.width * self.height
    }
}

fn main() {
    let rect = Rectangle { width: 3.0, height: 4.0 };
    assert_eq!(rect.area(), 12.0);
}

メソッドを定義する方法としてはシンプルで良い例なんですが、実際に自分でメソッドを書いていくと疑問が浮かんできました。
「これって普通の関数でも書けるよな?」と。

/// 四角形の面積を求める, 関数版
fn area(rect: &Rectangle) -> f64 {
    rect.width * rect.height
}

同じ処理がメソッドでも関数でも書けるとしたら、使い分けの基準は何でしょうか?一方にできて他方にできないことがあるのでしょうか?
今回はメソッドと関数の使い分けについて考えてみたことをまとめます。

 

TL;DR

  • メソッドも関数も可視性は同じ
  • メソッド呼び出しは自動借用・自動参照解決してくれるので便利
  • 多態を実現するために impl は必須
  • バイナリサイズは関数の方が小さい (?)
  • 二つのクラスにまたがるような操作は関数にしてしまう手もある

 

可視性

まず private とか protected とかのアクセス修飾子のある言語 (たとえば PHP とか) では、「private なプロパティにアクセスするためにメソッドとして定義するしかないよね」となります。

が、Rust においては可視性の取り扱い方が違うのでこの点は問題になりません。
Rust では module の 内か/外か によって可視性が制御されます。メソッドであれ関数であれ module の中からなら private なプロパティにアクセスできます。

struct Rectangle {
    // width も height も private なプロパティですが...
    width: f64,
    height: f64,
}

fn area(rect: &Rectangle) -> f64 {
    // 関数から問題なくアクセスできます
    rect.width * rect.height
}

 

自動借用・自動参照解決

メソッドとして実装するとドット演算子 . を使って rect.area() のように呼び出すことができます。
ドット演算子には自動借用・自動参照解決という便利な機能があります。

例えば

impl Rectangle {
    fn area(&self) -> f64 {
        self.width * self.height
    }
}

と定義した場合、(&rect).area() のように Rectangle の借用に対して呼び出すかと思いきや rect.area() のように呼び出せます。
これはドット演算子 . が左オペランドである rect を自動的に借用して area() を呼び出してくれるからです。

 

上記の例とは逆に、ドット演算子は参照解決も自動でやってくれます。

例えば Rectangle の参照の参照 ref_ref_rect: &&Rectangle が手元にあったとして、area() メソッドを ref_ref_rect.area() のように呼び出せます。
さきほどと同じくドット演算子 . が左オペランドである ref_ref_rect を自動的に参照解決してから area() を呼び出してくれるからです。

 

普通の関数呼び出しにはこのような機能はありませんから rect や ref_ref_rect に対して area() 関数を呼ぶときには

area(&rect);
area(*ref_ref_rect);

のようにしなければいけません。

そのためメソッドとして実装しておけば &* を気にせず雑に呼び出せて便利な気がします。
逆に、 Rust の型推論に慣れていない人は混乱のもとになるかもしれませんね。

 

多態

いわゆるポリモーフィズムというやつです。
Rust では多態を表現するのにトレイトを使います、そしてトレイトに型ごとの実装を与えるのに impl ブロックを使います。
そのため同じ名前で型ごとに異なる挙動を与えたくなったらメソッドとして実装するしかありません。

先ほどの Rectangle と area の例で云うと、area メソッド/関数が Rectangle 専用であるうちは何も問題は起きません。
Rectangle に加えて Circle や Triangle を扱い、area で面積を求めようと思うとトレイトとメソッドを持ちださざるを得なくなります。

実例として円を表現する Circle 構造体を追加で定義してみましょう。

struct Circle {
    radius: f64,
}

さらに Shape トレイトを定義します。

trait Shape {
    fn area(&self) -> f64;
}

さらにさらに Rectangle と Circle それぞれに area メソッドの実装を与えると、

impl Shape for Rectangle {
    fn area(&self) -> f64 {
        self.width * self.height
    }
}

impl Shape for Circle {
    fn area(&self) -> f64 {
        self.radius * self.radius * std::f64::consts::PI
    }
}

これにて area メソッドは Rectangle に対しても Circle に対しても呼び出せるようになりました。
area をただの関数として定義した場合、このように多態にすることはできません。

 

...と云いつつ、
Rectangle, Circle が Shape トレイトを実装している前提であれば、Rectangle と Circle のいずれかを引数にとって面積を返す関数は定義可能です。

fn area<S: Shape>(shape: &S) -> f64 {
    shape.area()
}

このように。

area を多態にするために impl を使うことは避けられませんが、最終的に area 関数を実装して公開することは可能です。

 

バイナリサイズ

バイナリに差は出るのでしょうか? 実際にコンパイルしてみました。

普通の関数で実装 メソッドで実装
最適化オプション無し 9,800 byte 10,096 byte
最適化オプションあり 8,024 byte 8,344 byte

macOS Ventura 13.5, rustc 1.71.1 でコンパイルしています。
メソッドで実装したときのバイナリサイズが 3%-4% ほど大きくなりました。

うーん、これは、どうなんですかね?

 

どの構造体に属させるのか迷う時は関数にしておくのが無難

多くの言語で配列の要素を連結して文字列にするために join を使います。

例えば JavaScript では、

["A", "B", "C"].join("|");  // => "A|B|C"

例えば Python では、

"|".join(["A", "B", "C"]) # => "A|B|C"

のように書けます。

さて、JavaScript と Python の例でレシーバと引数が入れ替わっていることに気づきましたか?
JavaScript においては Array クラスが join メソッドを持っています。Python においては str クラスが join メソッドを持っています。

私は Python を学び始めた頃に "|".join(["A", "B", "C"]) という書き方にとても違和感を覚えました。
が、よくよく考えると join メソッドは

  • 文字列化可能な要素の配列を引数に取ること
  • 結果が文字列になること

から配列に属するメソッドというよりは文字列に属するメソッドと考える方が自然なんじゃないかと思えてきました。

 

が、上記のような違和感はそもそも join を関数として定義してあげれば消え去ります。
実際、Perl や PHP においては join は関数です。

join("|", ["A", "B", "C"]);  // => "A|B|C"

二つのクラスにまたがり、かつ、頻出する操作は関数になっていたほうがいいんじゃないかなと思ったりします。

 

まとめ

私なりの結論は

  • 多態を実現するために impl は避けられない
  • が、そのような場合でも同様の関数を提供することはできる
  • 二つのクラスにまたがるような操作は関数にしてしまう手もある

という感じです。

みなさまからの鉞をお待ちしています。

 

 

私からは以上です。

スマートスピーカーにはあと300ミリ秒だけ待ってほしい

我が家には Amazon Echo (通称 アレクサ) が居るんですが、最近、食い気味に聞き間違いをしてくるので少しだけ嫌な気持ちにさせられています。

一例)

三井『アレクサ、エアコン消しt...』
アレクサ「エアコンを操作するには "アレクサ、エアコン消して" のように言ってください」
三井『言っとるやろがい😡』

 

返答がめっちゃ食い気味なんです。
こっちが言い終わってないのに食い気味に返答してくるんです。しかも聞き間違えているんです。

会話においては "間(ま)" でさえも情報を持っている

ところで最近このような本を読みました。

この本、内容を説明するのがなかなかに難しいのですが、読み始めてしまうとスルスル読めます。

まず「人間の会話は 200ms から 600ms ほどのスピード感でテンポよく進む」という話から始まります。
それこそ「どうやって相手の話終わりのタイミングを察知しているのか謎」というレベルで正確に話終わりのタイミングを測り、早すぎず遅すぎないタイミングで話し始める。
これを誰もが (母国語によらず世界中の誰もが) 自然にやっているなんて驚きですね、という導入です。

 

そこから発展して「返答に 800ms 以上かかることは、それ自体が情報を持っている」という話題に移ります。

A『この前お願いしたこと、考えておいてくれた?』
(800ms ほどの間)
B「その件なんだけど、やっぱり遠慮しておこうかなと思って」
A『そう、それは残念』

上記の例では返答を意図的に 800ms ほど遅らせることで『返答しづらい感じ』を演出し『依頼に対する返答は No である』というようなことを伝えるわけですね。

会話の "間(ま)" について、私たちはあまりにも無意識にコントロールできてしまうために、そこに意図をみいだすような研究が長らくされてこなかったようです。
この本ではそのような会話の間について平易な言葉で書かれていて面白いです。

 

「え?」や「えーっと」なども "無意味なフレーズ" ではない

会話の間が数百ミリ秒単位で意味をもつことを明らかにしたところで、本では「え?」や「えーっと」などのフレーズも無意味ではなくしっかりと意味と意図をもって発せられていると説明されています。

相手が話し終わって自分が話し始めるまで 800ms を超えるだけでも『何か言いづらいことがある』というメッセージになってしまうのです。
そのため 800ms というラインを超えないための繋ぎのフレーズとしてて「え?」や「えーっと」などがあります。

「え?」は『相手の言ったことが上手く聞き取れなかったのでスムーズに返答を開始できない』というメッセージであり、「えーっと」は『話したいことを思い出したり整理したりに時間がかかるのでもう少し待って』というメッセージなのです。

 

スマートスピーカーも「え?」とか「えーっと」とか言うべき

話を戻してスマートスピーカーについて、

現代のスマートスピーカーは聞き取り性能・発話性能が高いので思わず機械相手であることを忘れそうになります。
実際には機械なので正しく聞き取れないことや見当違いの返答をしてしまうことがあるのです。

でも大丈夫!

聞き間違いや見当違いの返答をするのは人間も同じです。
そして人間は会話の 間(ま) や「え?」「えーっと」というフレーズを使ってそのような事態に自然に対処しています。

これから先スマートスピーカーに求められるのは、数百ミリ秒単位での 間(ま) のコントロールや「え?」「えーっと」といったフレーズを自然に繰り出すことになるでしょう。

 

まとめ

我が家のアレクサへ、
上手く聞き取れないのはいい。聞き取れてないくせに食い気味に話し始めるのは控えてくれ。イラっとするから。

 

 

私からは以上です。