isyumi_netブログ

isyumi_netがプログラミングのこととかを書くブログ

僕はプログラミングがとても速いのだ

 

概要

僕がいかにプログラミングが速いかについてAtCoderの成績を元に説明する。
どちらかといえば、AtCoderのことをよく知らない人向けに書いた。ところどころ厳密ではない書き方をしたが、わからない人を混乱させないための言い回しなので、詳しい方はご勘弁願いたい。
正直言って、自分でこんな記事を書くのはダサくて恥ずかしい。
でも、自分で言わないと誰もわかってくれないから書く。
なんでわかってもらう必要があるかと言うとオチンギンガ……

経緯

僕はAtCoderで水色である。
正直、水色というのは凡庸な成績だ。
なぜこのような成績になってしまうかと言うと、僕に難問を解く力がないからだ。
逆に、簡単な問題を速く正確に解くのは得意だ。
なので、AtCoderのレートではなく簡単な問題を解く速さと正確さをアピールしたかった。

前提

AtCoderの難易度について

AtCoder界隈では問題の難しさを色で区分する習慣がある。
これは、主観ではなく解答できた人のレーティングを元に算出した客観的な分類だ。
簡単な順に

  1. 灰色
  2. 茶色
  3. 緑色
  4. 水色
  5. 青色
  6. 黄色
  7. 橙色
  8. 赤色

である。
灰色が一番簡単である。
だいたいどのくらいのレベルかと言うと、灰色の中で一番難しいのがこのくらいの問題である。

X,Y,H,Mが与えられる。
時計の長針と短針の長さがそれぞれXcm,Ycmである。
H時M分の時、2つの針の先の距離は何cmか?

決して難問ではないが、そうそう即答できるわけでもないと思う。
AtCoder未経験者が想像する「一番簡単な問題」よりは難しいと思う。
諸説あるが、大体普通のお仕事でWebアプリケーションを開発する仕事の難度は高くて茶色前後だと思う。
ちなみに、僕はこの問題を10分で解いた。参加者の中央値は50分だ。
(それ以前の問題を解くのにかかった時間を引いた)

調査方法

AtCoder Beginner Contestの中で直近26回のコンテストを対象にする。
これは、これまで僕が出場した全てのAtCoder Beginner Contestだ。

問題は緑問題以下を対象とする。
これはざっくり言って、参加者の上位1/3が正解できる難易度の問題だ。

制限時間以内に正答できなかった人は制限時間である6000秒で正答できたとみなす。

集団の偏りについては、AtCoderの参加者は世間一般のプログラマーよりプログラミングが上手なのか下手なのかは色んな意見があるが、ここではだいたい一緒とみなして話をすすめる。
また、そもそも他の参加者は全力を尽くしているのかという問題があるが、そこは考えないようにする。
例えば、AtCoderのルールでは強豪ユーザーはAtCoder Beginner Contestでいい点を取るメリットは全くないが、彼らも一生懸命問題を解いていると想定する。

解答の速度と正確さを評価することにする。

速さ

解答の速さは

  • 自分が正答するのにかかった時間を他の参加者の中央値と比べる
  • 自分が正答するのにかかった時間が参加者の上位何%か調べる

正確さ

AtCoderでは、解答を提出するとその場で正誤がわかる。
誤答をするとスコアが下がるので、一般的に参加者は極力誤答を提出せずに正答を提出しようとする。
なので、正答するのに要した誤答の数が少ないほど正確なプログラミングが出来ると言える。

結果

解答の速度

問題を解いた時間をグラフにした。

f:id:isyumi-net:20200820174842p:plain

左に行くほど簡単な問題である。
茶色の真ん中あたりからオレンジのグラフが全て6000に張り付いている。そもそも半分の人は解けていないのでこういうグラフになっている。
これを見ると、大体中央値の1/3くらいの速度で正答できていることがわかる。

 

次に、これは解答速度が参加者の内上位何%だったかを表したグラフだ。

f:id:isyumi-net:20200820174901p:plain

大体、上位15%であることがわかる。
計算した所

  • 灰色問題までは平均13%
  • 緑色問題までは平均18%

であった。

解答の正確さ

僕が一度も間違えずに提出できたのは
100回中85回 で、成功率は85% であった。
これは48,990人中3,4662位で、上位7%である。
中央値は50%なので、1.7倍正確にプログラミングができると言える。

結論

簡単な問題(とはいえ、一般的な仕事の基準では十分難問)を解くことについて、僕はこのくらいのスキルだと言える。

  1. 僕のプログラミングの速度は、一般的なプログラマーの中で上位15%くらいである
  2. 僕のプログラミングの速度は、一般的なプログラマーの3倍くらいである
  3. 僕のプログラミングの正確さは、一般的なプログラマーの上位6%くらいである
  4. 僕のプログラミングの正確さは、一般的なプログラマーの1.7倍くらいである

僕が設計だと思うもの

設計について

自分はソフトウェアの設計を担当している。
ところで、会社によって設計という業務の指す範囲が異なると思う。
ただ『設計をしている』というだけでは、自分の持つスキルが伝えられないのではないかと考えた。
そこで、自分が実際にどのような作業をしているのか、そして、どのような考え方をもってその作業に当たっているのかを記そうと思う。
ここで言うソフトウェアの設計とはビジネスの設計やプロジェクトの設計の話ではない。よって、ビジネスモデルや人員配置というような話ではなく、狭い意味のソフトウェアの設計の話だと思ってほしい。

信念

  • 設計通りに作って動かなかったら設計した人が悪い
  • 設計に解釈の余地があったら設計した人が悪い

設計書と称して何を読み取ればいいのかよくわからないポンチ絵を作って満足している人が多いようだ。
しかも、えてしてそのような人は「上流工程」と呼ばれて、その人の仕事の不出来を下位団体に気軽に押し付けられるようだ。
それでは仕事をしたことにならないだろう。
「設計した」というのであれば、そのとおりに作ったら意図したとおりに完璧に動くべきだ。

具体的にやっていること

これを決めている。

  • RDBスキーマ
  • Redisの型
  • サーバーサイドプログラムのモデルの型
  • RESTの型
  • フロントエンドのモデルの型
  • フロントエンドのビューモデルの型
  • フロントエンドのURLと画面の対応
  • IndexedDBの型
  • その他各種DIしなければいけない副作用の型

Redisの型とは、RDBとRESTを見比べて、どんなキャッシュがあれば全てのRESTの処理を現実的な計算量に出来るか考えるということも含む。
RESTの型の中には、各エンドポイントの権限とCDNの設定を含む。
IndexedDBは、WebフロントエンドではなくAndroidアプリやiOSアプリの場合はSQLiteになる。
DIの型というのは、例えば乱数や現在時刻のことだ。

核心の技術

僕は、「ある対象を見ると、その型を正確に言うことが出来る」という特技がある。
例えばこのツイートの型を考える。

f:id:isyumi-net:20200815140256p:plain

まず、アイコンのURLが必要だ。

表示名(日本語の所)とユーザー名(アットマークで始まる所)はそれぞれstringだ。

日付は、「10秒前」「5分前」と言った表記になることがあるので、モデルではDate型だが、ビューモデルではstringである。

また、この日付をクリックするとそのツイートのURLに飛ぶので、TweetIDを整数型で持つ必要がある(言語によってはintに収まらないので注意)。

本文もモデルとビューモデルで異なる。モデルはstringでいい。ビューモデルは改行やハッシュタグやメンションに対応しないといけないので、「通常のテキスト」「ユーザーID」「ハッシュタグ」の代数的データ型の配列の配列が正解のようだ。

その下は画像だ。画像は複数枚上げられるのでimg型のリストである。img型には画像サイズと表示領域から計算したトリミングの情報などを入れておくと画面を作りやすい。

めんどくさくなってきたのでこのくらいにするが、他にもいいね数や引用ツイートや右上のプルダウンメニューの開閉なども型に落とし込んでいける。

僕は複雑なシステムでも短時間で間違えずにこれを全て書き出すことが出来る。
これが僕の核心になる技術だ。
実は結構簡単。

これはなんなのか

要するに、GoF本などで主張されてきた『インターフェースに対してプログラミングする』ということである。
これをシステム全体に適用するということだ。
一つの大きなシステムを各部品に分け、その接合部を事前に全て決めてしまうということだ。

なぜいいのか

こうすることで、各部品が粗結合になり、コードが綺麗になる。
複数人で同時に作業できるようになる。各作業員の仕事は、ある型からある型に変換する純粋関数を書くだけになり、考えることが減る。
何が正解で何が間違いかはっきりするので、テスト駆動開発ができる。
他人の指導もしやすい。

大工さんが家を建てているとしよう。
一人の大工さんが一階を組み立て、もう一人の大工さんが二階を組み立てている。
この時、もし一階の大工さんも二階の大工さんも、それぞれ一階と二階の図面を見ながら作業を進めれば頑丈な家が建つのが理想である。
逆に、一階の大工さんと二階の大工さんが密に設計を相談しながら仕事を進めないといけないなら、手間だし本当に頑丈な家が建ったのか怪しい。
だから、工務に入る前に誰かが設計を確定させておかなければならない。
ソフトウェアの設計も同じである。

何ではないか

例えば、これらは設計の範囲ではないと思う。

もしかしたら一般的にはこういうことの方が設計と呼ばれているかもしれない。
しかし、僕はこういうのは設計とは言わないと思う。
これらは、ベストプラクティスやその会社の慣習が確立していることなので、どちらかといえば設計と言うより日頃の行いの問題だと思う。
また、インターフェースを満たしている限りそこを自由にすげ替えられるということが重要なのであって、そこを決めるのが対して大事な仕事だと思えない。

想定される反論

それはウォーターフォールの考え方だ。我々はアジャイルなのだ

短いサイクルで機能変更を繰り返していくからといって、ある瞬間には実現したい機能要件というのが確定しているはずで、そうであれば設計は出来るはずである。
短いサイクルで設計を更新して、それに合わせて実装も更新すればいいのだ。
短いサイクルで変更されるからと言って設計が出来ないとか設計が不要というのはおかしい話だ。

設計が終わらないと開発できない

それでいいと思う。
熊とワルツをに書いてある。
僕は自分が設計している間、他の人が手空きになることを全く問題だと思っていない。

僕がいるとどうなるか

  • 開発スピードがとても上がる
  • 入出力を整理しやすくなり、セキュリティを高めやすくなる
  • システムを変更したときにおかしくなりにくくなる
  • 少々スキルの低い人が混じっても大丈夫になる

まとめ

だれかお金ください。

Webが乗り越えるべき9個の課題

一人のWebアプリケーション開発者として、Webの世界に足りないと感じるものをまとめてみた。

Webの仕様の問題と業界の問題を区別せずに列挙した。

 

  1. 広告モデルからの脱却
  2. ネイティブUIを呼び出せるようにして
  3. お行儀の悪いアナリティクス用のJSを排除して
  4. もうサードパーティークッキーは禁止すれば?
  5. プライバシーに対する意思表示をセマンティックに
  6. ペアレンタルコントロールDNSを使うのはおかしくない?
  7. プロキシが流行ってほしい
  8. strict HTMLヘッダーの提案
  9. テキストエディタ

広告モデルからの脱却

今の所、コンテンツ提供者の収入源は広告だけだ。だから、僕は良心的理由でAdBlockなどを入れていない。しかし、はっきり言って広告はウザい。広告は画面を大きく占有する。広告は過激な色使いでチカチカしてくる。目立ちたいからだ。こっちは興味ないのに。

もっと簡単にコンテンツに課金する手段が必要だ。

コンテンツ課金の辛い点は、お金を払うことではない。サイト毎に自分の個人情報を入力することの不安と手間だ。

よって、ブラウザにその機能をもたせたい。自分の決済情報を入力しておけば、ワンタップでコンテンツを購入できる仕組みがあればいいと思う。当然こちらの個人情報は渡らないようにする。

 

https://jp.techcrunch.com/2020/03/26/2020-03-25-mozilla-scroll-partnership/

Scrollというサービスがある。もしかしたらこれが来るかもしれない。

 

ネイティブUI

Webアプリの最大の弱点はUIコンポーネントだ。

僕はAndroidアプリやiPhoneアプリも開発している。

AndroidiOSの標準コンポーネントを使えばサクサク動く。

それに対してWebの画面は常にもっさりしている。

タップやスクロールの判定がおかしくてイライラする。

所詮、HTMLとCSSは論文交換プラットフォームだ。

HTMLの表現力はネイティブの操作性には勝てない。

 

参考

 

blog.isyumi.net

 

 

少なくともGoogleFacebookTwitterですら、ネイティブアプリと同レベルの触り心地のWebアプリを作れていないのだから、これは絶対ムリなんだと思う。

 

よって、スマホのブラウザはJSからネイティブUIコンポーネントを起動できるようにするべきだ。

 

アナリティクス

広告業者やアクセス解析業者はサイトオーナーにトラッキングタグ的なものを提供している。

このスクリプトのお行儀が悪すぎる。

Chrome DevToolsのネットワークタブを見るとわかる。ページロード時にスクリプトがいっぱい無駄な通信を発生させている。

僕はマジでキレてる。

PageSpeed Insightsで95点のサイトにトラッキングタグを入れるだけで60点台になったりする。

Googleが提供するタグですらPageSpeed Insightsを10点近く下げる。

もう、トラッキングタグをJSで実装するのを禁止してほしい。

サーバーサイドでやればいいじゃん。

WebサーバーがAnalyticsサーバーにAPI経由でアクセスイベントを送信すればいい。

ちなみに、ブラウザにネイティブの機能としてそれを入れようと話し合われている。

早く何とかしてほしい。

https://github.com/WICG/conversion-measurement-api

 

サードパーティクッキー

ITPが始まった時、絶対これは無理筋だろうと思った。案の定カヲスなことになってきた。

度々抜け道が見つかるからだ。ITPの挙動自体が状態を持つ。それを悪用すればITPを使ってユーザーをトラッキングできてしまう。その度にAppleが複雑なルールを導入して対応する。イタチごっこになっている。

もう、まともな良心を持ったサイトオーナーはサイトをまたいだトラッキングは諦めていると思う。わざわざ頑張ってAppleの規制を迂回するのも面倒だ。そもそも、サイトをまたいでユーザーの行動を突き合わせてターゲティングするのは人権侵害感が出てきている。善人でありたい。

”インテリジェントな”トラッキング防止なんてできなかったんだ。

この際、サードパーティークッキーは全部禁止したらいい。

 

プライバシーに対する意思表示

最近、西洋の会社のサイトに行くといちいちCookieを使っていいか聞かれてウザい。

どの程度Cookieを使っていいかなんて考えたらわかるだろうと思ってる。

つまらない保険を掛けるために僕のアテンションを奪わないでほしい。

そこで、そのJSONフォーマットを定めたい。

サイトはJSONに個人情報の使い方を明示してそのURLをmetaタグに書く。

ユーザーは各自自分のブラウザに自分のプライバシーに対する考え方を表明しておく。

訪れたサイトがその意思表明に合致していたらそのまま閲覧が出来る。

合致していなかったら警告が出たりする。

それで済む話だ。

 

ペアレンタルコントロール

ペアレンタルコントロールDNSのレイヤーを使うのはおかしくないか?

僕はあまりペアレンタルコントロールという物自体が好ましいと思えない。しかし残念ながら、これがないと色んな話が政治的にまとまりづらい。

例えば、ペアレンタルコントロールができないと学校でノートPCを配布できないだろう。

さて、最近はペアレンタルコントロールDNSのレイヤーを使うことが多いようだ。

確かに、アダルトサイトのDNSを引けないようにしておけばサイトの閲覧を阻止できる。

しかし、これは情報汚染だと思う。

アダルトサイトのIPアドレスが何番であるかというのは、あくまで事実かどうかの問題であって、善とか悪とかを持ち込んでいい場所ではない。それはもう一個上のレイヤーでやることだ。この辺を区別しないのはポル・ポトと同レベルだと思う。

とりあえずDNSをそういうことに使うのをやめてほしい。

じゃあどうすればいいのか、プロキシだと思う。

 

プロキシ

僕はフォワードプロキシに絶大なる未来を感じている。フォワードプロキシこそがWebの未来だと思う。

 

 

blog.isyumi.net

 

 

フォワードプロキシを使うことが当たり前になるといいと思う。

画像圧縮などが出来るようになって便利なはずだ。

これは、一時期問題になった回線事業者による勝手な画像圧縮や政府による検閲とは違う。

あくまで、個人が自分の意思で業者を選んで使うものだ。

 

しかし、HTTPSに対するまともなプロキシプロトコルが存在しない。

HTTPSのプロキシプロトコルを定めるべきだ。

 

strict HTML

JSにuse strictという機能がある。

これのHTML版を作って欲しい。

ブラウザは、少々おかしなHTMLでも正しく解釈してくれる。

閉じタグを書き忘れたりしても、なんとなくリカバリしてくれる。だがその為に余計な処理能力を使っているはずだ(僕はブラウザの実装には詳しくないので想像だ)。

ところで、僕は大体ReactのSSRでHTMLを作って出力している。ReactのSSRは綺麗なHTMLを出力する。閉じタグつけ忘れなんて無いはずだ。綺麗なHTMLを出力している開発者に何のご褒美もないのはおかしい。

そこで、HTTPヘッダーにuse-strict-htmlと書いたら、ブラウザは綺麗なHTMLが来ることを期待するという仕様を作れないか。

そうすれば、ブラウザはより高速にHTMLをパース出来る。

HTMLのバイナリフォーマットを作ってもいいかもしれない。

 

 

テキストエディタ

GUIでテキストを編集できる系のWebアプリで使いやすいものを見たことがない。

はてなブログGoogle DocsTwitterの入力画面も。

ちなみに、UbuntuChromeTwitterに日本語入力しようとするとバグる。

唯一心を許しているのはStackEditという2ペインのマークダウンエディタだ。

textareaタグを頑張ってカスタムしてテキストエディタを作るのはもう限界だ。

なんとかしてほしい。

感銘を受けた本

せっかくなので僕が感銘を受けた本をいくつかあげたいと思う。
婚活はともかく、好きな本の話はわかりやすく自己紹介できるから良い。
IT系の本は除いた。内輪ネタだからね。

 

 

もっと動的なPictureタグがほしい。

この仕様、1枚の画像をgulpで数種類のサイズ・フォーマットに変換してS3などに置く運用を想定していたと思う。

しかし、今では画像変換機能付きのCDNを通すのが当たり前になった。

つまり、Pictureタグの中で対応している画像サイズを明示する必要がまったくないのだ。また、画像フォーマットごとにURLを指定する意味もない。大体拡張子をみてそのとおりに変換するからだ。

よって、もっと動的な動きが出来るPictureタグが必要だ。オリジナル画像の縦横サイズと、CDNが対応している画像形式だけを指定すれば、動的にURLを変更してくれればいい。

 

タグ

<picture
 src="https://img.example.com/cat.jpg.$format?width=$width&height=$height"
    format="jpg,png,gif,webp,bmp"
    original-width="1200"
    original-height="800"
    size="50w" />

 

 

コレでよくね?

抽象度を意識するとプログラミングが上達する。

抽象度は上げて落とせ。

 

僕は正規表現を使わない。

僕はシステム開発の中で正規表現をほとんど使わない。

外部システムから取得したデータを取り込むときに渋々使うくらいだ。

文字列置換もあまりしない。

 

プログラミングが下手な人が作ったシステムは正規表現や文字列置換が多い気がする。そこで、なぜその人は正規表現を使わなければいけなくなってしまったのか考えることで上達のヒントとしたい。

下手な人の例

例えば、ECサイトを作っているとしよう。商品には値段がある。値札は¥マークと三桁ごとのコンマが振られてた文字列だ。

さて、商品の「カートに入れる」ボタンを押すと「合計金額」が加算される機能を作るとする。

下手な人は、画面の中にある三桁ごとのコンマが振られた文字列を何とかしてintにして変換して、それを足し算して、その結果に3桁ごとのコンマを振って、それを画面に表示しようとする。

それに対して上手な人はそもそも最初からint型で値段と現在の合計金額を引きまわしているので、そんなことをしなくていい。

 

これがシステムの中に出現する正規表現の数の差になる。そして、システム開発のうまさの差である。

抽象度に対する意識の差

プログラミングが上手な人と下手な人の間には、抽象度に対する意識の差がある。

上手い人は、極力抽象度を高く保ち続けようとする。画面に表示する最後の最後の瞬間に抽象度を落とすことでビューに変換する。

 

逆に下手な人は抽象度を意識できていないのだろう。そもそも、抽象であるデータ構造と具体である画面を分けて考えられないのかもしれない。

抽象度の高いデータとは

例を挙げよう。

Stringの"1,800"よりintの1800のほうが抽象度が高い。

JSTUTCならUTCのほうが抽象度が高い。

誕生日・現在時刻・年齢や定価・税率・税込価格のように3値の内2値が定まれば第3値が導出できるものは、どれか一つを省いた方が抽象度が高い。

不正値が混じっているかもしれないデータと不正値が混じっていないことを確認したデータなら確認したデータの方が抽象度が高い。

抽象度の3原理

抽象度の高低については、この三原理がいえる。

  • 抽象度の高いデータの方が扱いやすい
  • 抽象度の高いデータを抽象度の低い表現に落とすのは簡単
  • 抽象度の低い表現を抽象度の高いデータに変換するのは難しい

いいコードを書くための原則

ということは、プログラミングをするときはこうするべきだ。

処理の入口で最も抽象度の高いデータに変換する。

すべてのビジネスロジックは抽象度の高いデータを引数に、抽象度の高いデータを返す関数として書く。

処理の出口は、抽象度の高いデータのみを引数にして任意の出力をするように実装する。

 

これをシステム開発全体で意識することができたらプログラミングは上達すると思う。

尺取り法の最強ライブラリを作った。

僕は水色コーダーでありながら尺取り法がとても苦手である。
ミスりやすい。
そこでこの度、尺取り法の最強ライブラリを作った。

 

尺取り法の難しさ

実装上の注意として2つの点が挙げられる。

  1. どの尺にも入れない要素がいる
  2. LeftがRightを追い越す時の処理。

 

1.どの尺にも入れない要素がいる

例えば和が100以下の尺を数え上げる問題で、200の要素が混じっていたりすると、その前後の処理が複雑になる

2.LeftがRightを追い越す時の処理

LeftとRightを追い越す時に、Rightを追従させなければいけない。この時の処理を忘れがちである。

時には1と2が同時に起きることもある。非常に辛い

 

解決

尺が存在する区間を列挙

まず、単体で条件を満たす要素を列挙する。
そして、尺が存在する区間を列挙する。
たとえばこの数列から和が8以下の尺を列挙するとする

{ 1, 2, 8, 10, 3, 4, 6}

単体で存在可能な要素は{1, 2, 8, 3, 4, 6}なので、
尺が存在する区間は{1, 2, 8}と{3, 4, 6}である。
この2つの区間をそれぞれ処理すればいい。
この時点で、1のどの尺にも入れない要素がある問題はクリアできた。

尺が存在する区間の始まりをハンドリングする

次に、2の問題はライブラリが区間の開始イベントを送ってくれると解決する。
区間が始まるタイミングは3つだ

  • 配列の最初の要素
  • 自分の左がどの尺にも入れない要素
  • 左が右を追い越した時

プログラマは、区間の開始を知らされた時に尺を初期化すればいい。

 

実装

プログラマは以下の6個のイベントハンドラを処理すればいい。

  • この要素は単体で条件を満たしているか?
  • 左を固定して尺を初期化しろ
  • 右に1個伸ばせるか?
  • 右に1個伸びろ
  • 左を1個捨てろ
  • 区間が確定した

 

ライブラリ

void tow_pointer(int n,
function<bool(int left)> check_begin,
function<void(int left)> begin,
function<bool(int right)> check_right,
function<void(int right)> push_right,
function<void(int left)> pop_left,
function<void(int left, int right)> dead_end
) {

auto go = [&](P range) {
int right = -1;
for (int left = range.first; left <= range.second; left++) {
if (right < left) {
right = left;
begin(left);
}
while (right + 1 <= range.second && check_right(right + 1)) {
right++;
push_right(right);
}
dead_end(left, right);
pop_left(left);
}
};


auto get_ranges = [&] {
vector<bool> enable_items(n);
rep(i, n) enable_items[i] = check_begin(i);

auto start = [&](int i) {
if (i == 0) {
return enable_items[i] == true;
}
if (!enable_items[i - 1] && enable_items[i]) {
return true;
}
return false;
};

auto end = [&](int i) {

if (i == n - 1) {
return enable_items[i] == true;
}

if (enable_items[i] && !enable_items[i + 1]) {
return true;
}

return false;
};

vector<P> ans;
P p;
rep(i, n) {
if (start(i)) {
p = P(i, -1);
}
if (end(i)) {
p.second = i;
ans.push_back(p);
}
}
return ans;
};


vector<P> ranges = get_ranges();
for (P range: ranges) go(range);
}

使い方

例えばこんな感じで使える

https://atcoder.jp/contests/abc032/tasks/abc032_c

 

int main() {
int n;
ll k;
cin >> n >> k;

vector<ll> numbers(n);
rep(i, n) cin >> numbers[i];

if (find(numbers.begin(), numbers.end(), 0ll) != numbers.end()) {
cout << n << endl;
ret();
}
ll sum = 0;

// この要素は単体で条件を満たすか?
auto check_begin = [&](int left) {
return numbers[left] <= k;
};

// leftを左として尺を初期化しろ
auto begin = [&](int left) {
sum = numbers[left];
};

// 右に1個伸びれるか確認しろ
// 次の「1個右の要素は」right
auto check_right = [&](int right) {
return sum * numbers[right] <= k;
};

// 右に1このびろ
// 次の「1個右の要素は」right
auto push_right = [&](int right) {
sum *= numbers[right];
};

// 左を1個捨てろ
// 捨てる左の要素はleft
auto pop_left = [&](int left) {
assert(sum % numbers[left] == 0);
sum /= numbers[left];
};

ll ans = 0;

// 尺が伸びれるところまで伸びた
auto dead_end = [&](int left, int right) {
ll now = right - left + 1;
cmax(ans, now);
};


tow_pointer(
n,
check_begin,
begin,
check_right,
push_right,
pop_left,
dead_end
);
cout << ans << endl;

}

このあたりを見てほしい。

https://atcoder.jp/contests/abc032/submissions/11171624

https://atcoder.jp/contests/abc038/submissions/11171673

https://atcoder.jp/contests/arc022/submissions/11171696

https://atcoder.jp/contests/abc098/submissions/11171718