はてなキーワード: ソースコードとは
プログラミングで主にやる事は下記の2つ。
①IFでAかBを選択させてどっちかの設定を実行
②Whileで決められた回数分繰り返す
とてつもなく複雑で冗長な処理によって実行されている。
わかりやすいので画像処理でいうと、数十万から数百万の画素(RGBAの24bitで表される数値)を小さなブロックに分解し、数学的に周波数の重なりとして計算して変換、含まれる頻出パターンをテーブルにして圧縮伸張を行なう。みたいなことが瞬間的に行われている。
「まさかそんな事できるわけないだろ」というレベルの処理が実際に行われており、これまた直感的でない。
だからそれをどう書くんだよ。という答えはコレ。有名なjpegの実装だ。
libjpeg というライブラリを書くことはできるだろうか?画像の圧縮の理論から考え始めることはできるか?
正直無理だ。自分はプログラマだがそんなに数学が得意ではなく、頑張ったとしても下手するとコレを作るのがライフワークになってしまい、他のことができなくなる。
例えばブラウザを0から作るとして、jpegの処理以外にも画像だけでpngとかgifとかwebpとか、その他もろもろとてつもない作業が必要になる。
「とてつもなくて想像もできないので流石に無理だろう?」
いや、でも、実際動いてるのよ。ここ何十年、コツコツと積み重ねて実現している。
「積み重ね」とはライブラリであったりフレームワークであったりOSであったりする。
「どういう風になっているのか」
外部に向けたインターフェイスがどうなっているのかは理解する必要がある。「使う」ために必要だからだ。
この2つは分けて考えなければならない。
ちなみに、たとえばChromeのコアであるChromiumはのコードはコレだ。
つまり言いたいことは、実際に動くアプリケーションというのを作りたいのにも関わらず
プログラミング入門書は、これで判定と繰り返しという基礎ができますと言うだけ。
これがもう滅茶苦茶イライラする。
「これで判定と繰り返しという基礎ができます」というのが基本的な理論(定理的なもの)で、その他に必然的だが唯一無二ではないベストプラクティスというものがある(法則的なもの)。
後者をうまく説明する入門書に出会っていないんだろうな。という印象。イライラはやめよう。つかれる。
ベストプラクティスはいろいろあるのだが「層の構造にする・レイヤーに分ける」というのは重要なアイデアだ。
libjpegというのはjpegの処理を行う「ライブラリ」だ。他のアプリケーション...たとえばブラウザはこのライブラリを「使う」。
ブラウザではjpeg画像の圧縮展開というとてつもなく難しい処理を「libjpegの使い方」の理解までで済ませ、過去の蓄積であるlibjpegのコードを利用することで真の意味で0から実装しないようにしている。
この場合、libjpegが「低レベル・低レイヤー」の存在であり、中身については「使い方」つまり「仕様」の理解までしか行わないことで、実際に作りたいものを作れるようにしているわけだ。
完成しているプログラムは二例ほど挙げたがどうですかね?
複雑なことをする、特に低レイヤーのコードはとてつもなく難しい。
でも、とりあえずこんな感じのコードなら解るよね?
こういうレベルから理解して、ちょっとずつ難しい処理を学んでいくしかない。
ハードルは高いんですよ。実際。
なので、木材からだと難しいからプレハブのキット的なものを探すとか、ログハウスのカタログを読むとか、あるいは100人乗れる物置を買うのがいいかもしれない。そういうところから始める。
それらがフレームワークであったりライブラリであったりする。目的に合うものを探して、自分がやりたいことをどう実現するかとにかく考える。
「テキシコー」https://www.nhk.or.jp/school/sougou/texico/ で言われる通り、「小さく分けて考える」「手順の組み合わせを考える」「パターンを見つける」「大事なものだけ抜き出して考える」「頭の中で手順をたどる」をひたすら実行する。
unityはコードが公開されているので、本当に読みたいなら。。
オブジェクト指向は一旦忘れよう。
オブジェクト指向の「隠蔽」というのは層の構造が持っている重要な要素ではあるけど、「低いレイヤーについて考えない」のが基本的な作戦だという理解の方が重要だ。
前述の通り「できる限り作らない」んですよ。「使う」だけ。知るべきことを最小化する。
そして本当に作るべきものに関しては、利用する下のレイヤーのライブラリなりを探して・仕様を理解して、どう組み合わせてfor, if, あるいは計算させれば実現できるのかをひたすら考える。
単に翻訳がしたいのか?表示に割り込む方法を知りたい?日本語に翻訳するのは実行時なのか開発時なのか?
要求される表示エリアが言語によって異なるために、デザイン調整が必要になる問題をどうするか?
分解が甘いので何をしたらいいか調べることができないんだと思う。
ちなみに、アプリ内の文言というのはアプリの外部から変更できないように実装されている事が多いので、利用者が上書きする仕組みはかなり難しい。
AndroidなりiOSの仕様にもそのへんに割り込める機能はないはずなので、OSの開発に入っていく必要がある。結構大変だとおもう。
アプリの開発者が、そういう機能を備えた多言語化のためのライブラリを使うようになれば実現可能ではあるので、そっちの方向で頑張るのがおすすめだが、英語圏の開発者には多言語化のモチベーションが低いという基本的な問題はあるのよね。
この辺の「できる・できない・むずかしい」の判断は、いろいろな勉強をすると常識としてある程度みえてくる...気がする。
ついでに。ウェブサイトやウェブサービスの翻訳だとこういうサービスがあったりする。
ブラウザはページの描画処理のなかに割り込む余地が大きく取ってあるので、ブラウザのExtensionとかならできることがいくらかあるかもしれない。
個人的に気に入らない話はOSのアップデートは使いやすくなるからとてもいい事だからすぐにやった方がいいと宣伝されている事。
まあ、半分は嘘だよね。古いものが残っていると先に進めないんだよ...。
現在のクライアントOSは、巨大なプラットフォームのパーツの一部として理解したほうが正しくて、古いパーツが残っているとツライんですよ。
そして「サービスを受けるための道具であって、あなたが何でも好きにできる機械ではないです」みたいな世界になりつつあって、ちょっと問題と言われてもいる。
これはかなり困った傾向なんだけど、全体としての流れはあんまり変わりそうにない。
オブジェクト指向好きですな...。ここではオブジェクト指向は特に気にしなくていいですよ。
とてつもなく複雑なことをやっているために、すべてのバグを潰すことはコストが高すぎてできないんですよね。
それよりバグは未来を先取りするコストと考えて、本質的に価値のある機能を増やしていくというのが基本的な方向になっている。
だからパソコンはたまに不具合を引き起こすんです。しゃーない。
しかし中途半端に理解している老人などは、そんなことじゃ分からん。自分に分かるように説明しろと言い出す。
説明は出来る。しかし相手はイライラするし理解されない。よって説明をしてはいけないという状況に追い込まれる。
ここでどうすればいいのだと理解不能に陥る。
まあ、説明って得てして難しいよ。しゃーない。
そのとおりです。
オープンソースのプロダクトなら原理的には調べられるけどね。Androidとかはオープンになってる。
それを許容することで先に進んできているという事実は受け入れたほうがいいと思う。
「把握・理解可能な範囲」に留めていたら、数十年前のコンピュータの世界から抜け出せなかった。
deep learningの世界ではそれがより一層進むかも。この辺は詳しくないけど。
ここでの「理解」についてはそのとおり。これはもう諦めるしかない。
これが常にある。IT関連は常に新しい情報が出てくるのでそれに送れると無知になってしまう。
なんでこんなことも分からないんだとか言われ放題で、IT系の企業に努めている人は常に新しい知識を入れられる
面倒くさがらない人が向いている。
「面倒くさがり」の方が問題に気づいて「頑張って面倒じゃなくする」ことができるので、プログラマにとっては美徳なんて言われますけどね。
同時にくじけないとか諦めない、しつこいみたいな素養は必要かも。
応用まではとろうな。がんばれ。
このへん自分も知らんですよ。べつに全部知っている必要はない。
(追記: はてな記法の引用すらもさっきまで知らなかったしな!そんなもん)
層の構造をとっているということと関係があるんですが、仕様が変わると、その上に乗っているものを全部なおさないといけないんですよね。
でも革新のために互換性を捨てなければいけないケースも多い。このへんはハードでもソフトでも同じ。
そして、メンテのコストが上がっても使い続けたほうがトータルで安上がりという場合は、古いものが残ってしまう。
あるいは「(多少の問題はあっても)動いているものは変えるな」という経験則から意図的に残す場合もある。
西暦2020年にもなって、プログラミングが簡単には出来ないし、ハードウェアの規格も完全に統一はされていない。
というかプログラミング言語自体多すぎる。ソフトウェアはデファクトスタンダードのモノ程度は知っているが、
ぜんぜん完成していない荒っぽいものを目にしているのだと理解したほうが的確。
それなのに毎日理解のできないパソコンやスマートフォンを使っている。
オブジェクト指向のおかげ様だがオブジェクト指向に対して無性に腹が立つ。
自分の全く知らない場所でいけしゃあしゃあと演算を行い、そして結果を出す。それも大半が正しい結果で
利便性が抜群だ。些細なミス(バグなど)はあるが圧倒的に利便性が勝っている。
そんな道具に踊らされている自分が滑稽だ。理解できない愚かな自分は正に機械の奴隷のようだ。
本当に理解できない。辛い。
勘違いしてはいけないのは、それらはすべて先人の努力の蓄積によって成り立っているということ。
「よくわからないけど存在している道具」ではなくて、信じられないほど複雑だけど、多くの人々の行動によってなんとかかんとか実現した道具なんですよ。
「オブジェクト指向のおかげ様」じゃないんです。(もちろんオブジェクト指向というのも大きな発明の一つですが)
そしてブラックボックスとして使うのは多くの場合正しいです。そこは諦めましょう。
でもエンジニアとしての立場からは、その裏に隠れているとてつもない技術や思考の蓄積に感動してほしいなと思う。
人類がこんなもん作れたのって、かなりすごいよ?
どんなに優れたツールや設計思想などがあっても、使う奴がダメだと全く無意味。弊社もWebアプリを作ってて、RESTだのFluxアーキテクチャだのいろいろ導入を試みたが、ほとんど無駄に終わった。
どんなクソ組織でも効果があると確信持って言えるのは上の3つだけ。1つ目は初歩的すぎると思われるかも知れないが、筆者の想定するダメな組織・ダメなプログラマというのは、このレベルの連中を含む。
静的型付け言語(サーバーサイドならJavaやC#、フロントエンドならTypeScript)を使わせれば、少なくともコンパイル時に分かるエラーは修正させられる。
というか、ダメなプログラマに動的型付けの言語は触らせてはいけない。必ずそのプロジェクトは半年後には保守できなくなる。
テストは強制的に書かせるし、テストのないクラスや、通らないテストあったらコミットできないようにする(それは容易にできる)。
もう一つの方法は、そもそも優秀なエンジニアしか参加できないようにすること。たとえば、Scala、Haskell、Erlang、Common Lispなどで書かれていれば必然的にそれが分かるエンジニアしか開発できないし、こういう言語を自主的に学習しているエンジニアは優秀である可能性が高い。
テキストの説明が理解できないとき、学習者がすべきなのは自身の理解を正すことであって、自己流の解釈を思い付くことではない。つまり、
といったことをすべきなのであって、自分の腑に落ちるQiitaの記事とかを探すことは、全く理解に近づいていない。むしろ遠ざかっている。
というか、「明らかに分からない用語などがあるのに、そこを回避して全体を理解しようとする」のは、プログラミングに限らず勉強法として根本的に間違っている。
かつて、どうしても「コメント」の意味が理解できない新人がいた。
要するに彼は、プログラムの処理に関係の無い機構が存在する意味が理解できなかったらしい。
「コメントは、コードでは表現できない実装の意図をソースコード中に記述するときに用います」
などと説明してみても、
... // 15の倍数を先に判定しないと、たとえば15がFizzになってしまう if (n % 15 == 0) { return "FizzBuzz"; } else if (n % 3 == 0) { return "Fizz"; ...
「コメントは、処理をコメントアウトしてデバッグするための機構である」
と言う結論に達したようだった。勿論、普通のプログラマなら誰でも知ってるように、そういう使い方は良くない。
「プログラミング言語のあらゆる機能が、プログラムの何らかの処理と対応している」
という誤ったメンタルモデルを正すことである。それを放棄して、自分にとって都合の良い出典不明の情報を鵜呑みにしたのが、そもそもの間違いである。
こういうことは、何も新人に限った話ではない。自分では一丁前のつもりのプログラマにも、ライブラリ等の全く見当違いな使い方をしてくる奴がよくいるのである。
そういうのは、自分の経験のある別の言語の○○という機能に対応している、と勝手に思い込んでいたり、あるいは、実装とセマンティクスの区別ができず、インタフェースのような処理と直接関係ない機能が理解できなかったりする。
要するに、不明点を正しく理解することを放棄して、自分に都合の良い解釈を得て早合点しているのである。
そういう人はプログラマには向いていない。
プログラミングできる気になった自称中級者は、ソースコードに共通のパターンが現れると決まって、その処理を関数などに共通化したがる。
たしかに、そうすることでソースコードは短くなるし、一見して保守性が上がったような気になるのだが、それは間違った作法だから止めろ。
細かいこと言っても伝わらない自称プログラマが読んでることを想定して、先に結論を簡単に書いておく。
なぜコードを共通化するのがいけないのか。理由は簡単だ。要するに、コードが似ているのは単なる偶然であって、それらは別の処理だからだ。
別の処理だから共通化するのはおかしいし、もし共通化した処理の一方のみ仕様が変わった場合、その修正は他方にも影響してしまう。つまり、保守性が下がっている。
たとえば、同じプロジェクトの中に、10%の消費税を加える処理と、10%の金利を加える処理があったとする。この2つの処理はともに元の金額を1.1倍する処理であり、全く同じ処理であるが、共通化してはいけない。
これらを共通化してしまうと、たとえば金利が8%に変更になったとき、金利計算の処理だけではなく、消費税を計算している箇所すべてを変更しなければならなくなる。
実際のアプリケーションでやりがちなのは、複数の処理の「事前処理」「事後処理」などを1つの関数にして、呼び出し毎に細かい挙動を引数で制御するようなパターンだ。
これは結局、改修を重ねる度に「事前処理」「事後処理」の内容が使用箇所によって全く異なるものとなり、それに対応するために
他にも、GUIアプリでユーザーの応答を待つDialogクラスなんてものを作って、使用箇所ごとにメッセージやボタンに割り当てる処理などを切り替えることがある。
これも間違いなく、プログラムが成長するにつれて破綻する。たとえば、ある場所のダイアログは、表示するメッセージがテキスト形式のみではなくなり、脇に画像を表示するかどうかのフラグをコンストラクタに渡したり、Dialogを継承させて表組みを表示するTableDialogサブクラスを作ったりすることになる。ボタンが「OK」と「キャンセル」の2種類の場合じゃなくなって、表示するボタンの数をコンストラクタに渡したり、ボタンに割り当てる処理をリスト形式で渡したりし出す。
こうして、最初は良い設計に見えたDialogクラスはどんどん複雑になる。こうなった原因は明らかで、本来は異なるものを共通化したからだ。おかしな色気を出さずに、素直に別々に実装しておけばよかったのである。
プログラミングをする上で「コードを共通化する」なんてことは意識しなくていい。それよりもプログラマがすべきことは、処理に適切な名前をつけることだ。そのプログラムにおいて「単なる変数の操作」を超えた意味のある処理には名前をつけろ。そして、同じ意味の処理なら同じ関数を使うし、違う処理なら違う関数を使う。それだけだ。コードが共通化できるかどうかなんて全く関係ない。
変数、関数、クラス、名前空間等が再利用のための機構だという先入観は一旦捨てろ。それらの真の意義は、「関心の分離」にある。つまり、実装を隠蔽し、その意図を抽象するために存在する。たまに勘違いしてる奴がいるが、別に1回しか使われない関数とか、1行しかない関数はあってもいい。というか、この原則にしたがって設計すると、ほとんどの関数(or メソッド)は数行になる。
上の消費税の例で言えば、「消費税を加える」「金利を加える」処理は、明らかに単なる算術演算以上の意味のある操作だから、関数化する。そして、それぞれの実装は当初の仕様では奇しくも全く同じになる。消費税を加える箇所では前者の関数を呼ぶし、金利を加える箇所では後者の関数を呼ぶ。
これはこう言い換えることもできる。消費税を加える関数を変更するのは、消費税の計算処理が変わったときのみであり、金利を加える関数を変更するのは、金利の計算処理が変わったときのみである。つまり、すべての関数は、それを変更する理由がただ1つになるように設計しろということだ。
こういうアプローチでプログラムを書くと、ソースコードはあたかもそのアプリケーションのドメイン特化言語で書かれたかのような見た目になる。
また、一つ一つの関数は小さく、理解しやすく、テストやデバッグも容易になる。そして、結果として再利用もしやすくなるし、プログラムの変更も容易になる。
本当に嫌になる。
見よう見まねでコード書いてるだけのゴミはプログラマ名乗らないでくれ。
他にもいろいろ書きたいこと(ネットワーク、データベース、セキュリティ、OS、ハードウェア等)はあるが、ソースコードが書けるという点だけにフォーカスすれば、この程度は最低限できないと困る。
上に書いたようなことが完璧にできると断言できない奴が、プログラマを名乗るのがいかに非常識で恥ずかしいことなのか、よく理解して欲しい。