はてなキーワード: グローバル変数とは
https://type.jp/et/feature/26796/
この発言にもあるように「コードを綺麗にする=読みやすくする」ってことだと勘違いしてる
コードを綺麗にするのは「バグを少なくする」ためであって読み手のためじゃない
グローバルに一文字変数を使って困るのは「どこでそれを触ってるか分からないから」であって「読みにくいから」ではない(まぁ読みにくいけど)
特に昔だとLintもないし変数の参照先を探すのはgrepぐらいしかなくて
$iとかだと$iiもひっかかるし$iの後ろにスペースがあったり無かったりするともう探すのは不可能に近くなる
それでも動いているなら最悪問題無いんだがバグの修正時にめちゃくちゃ困って
「作り直すしか無いな」
ってなるのでビジネス的にも大きな影響が出る
「どんなコードでも動くコードを作るのが正しい」「done is better than perfect(完璧を目指すよりも、まずは終わらせることが重要) 」のスタンスが効率的だろうなぁ、、と思うおいらです。
これも元の言葉の意味を曲解していて、「終わらせることが重要」というのはバグがあって良いわけじゃない
例えばログインボタンを実装したときに、ユーザー名とパスワードに何を入れてもログインできる状態にするのも「終わらせること」だし
ただこのままリリースできるわけではないし、プロダクトとしては「終わっていない」
パスワードを平文で保存して実装するのも「終わらせること」ではあるけれどそのままリリースしていいわけではないし
下手に動いてしまうとそのままリリースされたりもするのでよりタチが悪い
この言葉の重要なのは「better than perfect」の方であって「done」の方ではない
全てを完璧にする必要は無い(し、そもそも完璧は定義できない)ので「perfectでなくていいよ」というだけ
バグがあったり不十分だったりセキュリティ不備があって良いわけではない
毎日論理構成の中に浸ってる人は、推理小説は向いてるかもしれないですね。初期にちょろっと設定したグローバル変数が、最終的な結果に大きく影響してくるとか、「ここで使われてるのかー」みたいな感慨とか。
残念ながら「ああ、まともなコードしか読んできてないんだな」としか思えない
例えば「ユーザーの名前と住所は設定できてるから、性別を設定できるようにして」という依頼があって
コードを確認してみるとuser1, user2, user3という変数が100個用意されていて、user1.name = 'hoge', user2.name = 'gaga' って感じで100行書いてあって、更に住所で100行あって、性別も同じように100行追加しろっていうコードを読んだことが無いんだろう
そしてそのコードのどこかで住所設定が間違えているから確認しないといけないような作業をしてないんだと思う
小説で言うと同じ文章が100ページ続いていて、その中のどこかの漢字が違っていて、そいつが犯人、みたいな推理小説、面白いか?
他にも足したり引いたりこねくり回された変数値が最後に定数値で上書きされてたり、UserのオブジェクトがいきなりWeatherのオブジェクトに置き換えられていて、name属性に晴れとか雨のデータが入ってたりしたことがないんだと思う
汚いコードは伏線を回収しないし最終的に犯人も分からないし無駄に長いので推理小説には全く向いてない
で、やっぱりこういう汚いコードの問題は「バグが混入しやすいかどうか」であって「読みやすいかどうか」ではない
下手するとuserオブジェクトを100行ずつ書いてくれてる方が読みやすさはあるかもしれないが
「user36だけ住所が設定されていない」といったバグが混入し得るし、それを確認するのに多大な労力を必要とする
人間は誰もが間違いを犯すので誰もがバグを混入させる危険性があるんだけれど
その危険性は最大限まで下げるように努力するべきだし、インシデントを引き起こすことでビジネス的なインパクトも大きい
競プロと機械学習系のクソコード・クソジャークっぷりが取り立たされてるけど、クソコード・クソジャークっぷりは何も競プロerと機械学習erの専売特許ではない。
いろんなアプリケーションのメンテ(バグ取りとか細かい機能追加とか)を何度か経験してきた。
主にテキストファイルとかCSVファイルとかExcelファイルとかを入出力するものばかりだったんだが、その大半がファイルの拡張子をグローバル変数化していた。
こんな感じ
const TxtFileExt = ".txt"; const CsvFileExt = ".csv"; const ExelFileExt = ".xlsx";
outFileName = 〇〇 + ×× + "ABCDEFG" + TxtFileExt;
みたいな指定をしなきゃならない。
あと、プログラム言語の標準的なメソッドのあらゆる引数も全部変数で定義されてて、そのまま渡すのは禁止、みたいな規約になってる。
たとえば引数が三種類(true、false(未指定時のデフォルト値)、任意の数値(ただし当該プログラムでは0、10、25以外指定不可))しかないやつはこんな感じ。
const 〇〇ParamTrue = true; const 〇〇ParamFalse = false; const 〇〇ParamIntMin = 0; const 〇〇ParamIntMid = 10; const 〇〇ParamIntMax = 25;
const charCodeSJIS = "Shift_JIS"; const charCodeUtf8 = "UTF-8";
以前関わった改修内容に「××の処理は開始時と終了時にそれぞれUTF-8(BOMなし)形式でログを出力する」みたいなのがあって、普通に文字コード指定する部分に「UTF-8」で直に書いたら、規約に従ってないからとコードレビューで指摘されて差し戻されたんだけど、そもそもこういう規約って何の意味があるの?
コードの重複があるわけでもない状況で、コードを関数ごとに分離するメリットデメリットを知りたいという話ですよね。
コードの重複がある場合に関数などに切り分けていないと、同じコードを何度も書くことになり、不具合があった時にコピーされたすべての個所に変更が必要となるというデメリットがあるので理由がわかりやすいですが、重複が無いとその点が不明確ですね。
画面に収まらないサイズのコードは複数の関数に分割するのが一般的だとは思います。
理由は元増田も書かれている通り、長いと理解の限度を超えるからです。
コードは意味があるまとまりで短ければ短いほど理解がしやすいと思います。
グローバル変数を使わないようにすると、入力・出力が関数を読むだけで明確にわかるので、さらに理解がしやすいです。
また、関数に分けておけば、関数が仕様通りに動くかの確認するユニットテストも簡単に書けます。
ユニットテストでは関数がさらにほかの関数を呼び出している場合、呼び出される関数の代わりにテストダブルを用意することもあります。
分割して、複数の関数を呼び出すようにすることのデメリットは、
下手糞が切り分けるとなんでそういう切り分けになったかわからないところで切り分けられてかえって可読性が損なわれるとか、
関数の機能が拡張してより多く・あるいは少なくの情報が必要な時に関数インタフェースの変更が必要になることとか、
関数を置いているファイル内の場所を変えたときにバージョン管理システムが追っかけてくれないことがあるとか
くらいでしょうか。
いずれにせよ、分割するメリットの方がデメリットを上回ることが大半なので、大抵は機能ごとに分割して小さい関数を作り、それをメインからは呼ぶようにすると思います。
まず、関数の名前をやっている工程を表すものにすることですね。
「データの取り込み」 とか 「データの突合せ」とかを明示すると、それを呼んでいるということはそういうことをしてくれると思うので。
また、関数が何をしてくれるのかも関数のコメントとしてつけておくとよいと思います。
例えば、
filename引数で指定されたファイルからデータを取り込み、JSONフォーマットで返す
返値: JSONフォーマットされた取り込まれたデータ。例: [{'employee name': '山田 太郎', 'employee id': 1}]
例外: filenameを開けない場合はFileOpenError、JSONにコンバートできなかった場合はConvertError
みたいなコメントをつけておくと何をする関数なのかわかるので、その機能を調べたいとき以外は読まないでいいかなと。
あと、コードを連続で読みたい場合、ソースを解析してタグジャンプをつけてくれるツールやらIDEやらを使うことが普通だと思います。
これはどういう意味でしょうか?同じものを表すのに関数ごとに別の変数名を付けているとか?
もしそうだとしたら、使っているプログラミング言語の制約やプログラミング規約によるものなのでしょうか?
ある関数のローカル変数が他の関数のローカル変数に影響を与えることは無いはずなので、ローカル変数は大抵適当な名前が付けられるイメージです。
今時のプログラミング言語なら変数のスコープが関数の中にとどまるような書き方ができると思うのですが。
関数インタフェースを定義し、そこにいちいち引数を書くのが面倒というなら...まあ、それは必要税って感じがします。
そこに引数を書いておくことでこの関数が何に影響されるのかわかるので。
参考までに。
複数の機能で成り立っている長いコードを分割して実装することののメリット/デメリットを教えてほしいです。
「コードの分割」が指してることを大まかに言うと、「メインの関数は各機能を呼び出すだけで、実際の機能の部分はサブルーチンとしての関数(って表現が正確かも謎)に持たせ、サブルーチンを順次呼び出すことで総体としての機能を成す」ような方式にするってことです。
より具体的に言うと、1.データの取り込み2.取り込んだデータの突合3.帳票の出力の3手順を別々の関数とし、メインの関数から1,2,3の手順の関数を順次呼び出すという具合です。
上記の方法と、全ての機能を詰め込んだ一つの長い関数にする方法と、どちらが結局よかったのかなと思っているんですね。
今のところ私は自分のわかりやすさのためにコードを分割する書き方をしています。理由は、1機能1関数で分けておいた方がステップインじゃないですけど「ここまでは完走できた」の切り分けがしやすいのかなーと思うのが一つ。もう一つは単純に上下に長くなっていくとどの変数がどれでと特定していくのが辛いってのがあるためです。
ただこの方法にも問題があると思っていて、一番はメイン/サブ関数間で右往左往するので今やってる工程が何なのかがよくわからん、他人が読むならなおのこと、ってことです。一応の対処として、メインの関数は目次的に「総体としてこのような機能を持っている、また分割した関数の機能はそれぞれこうである」とコメントアウトし、サブの関数にも「この関数はここからここまでの作業をします」とコメントアウトすることにしています。
あとは関数ごとに変数をいちいち定義し直すのがだるいみたいなのもありますね。グローバル変数は後々の修正とかのために使わないようにしています。
自分では思いつけてない部分でコード分割することのやばみってあるのかなーと思ったので質問させていただきました。近々退職する予定なので、他人への引継ぎって観点からどうなのかなと思っています。
以下自分語りです。語彙とか概念のインストールが足りないと適切な調べ方ができなくて困るんですねー。あと問題があることを認識できなかったり効率悪かったり車輪を再発明したりとか。
私は無学のバイトなんですが、あるとき上長から「暇なら適当にエクセルでも勉強しといて」と漠然と言われて、このVBAっちゅうもんを学べばええんか?と勘違いしたのが始まりでした。本当はセルの結合とか別のセルを参照するとか、エクセル方眼紙的なものをある程度作れるようになってほしかったらしいです。折角なのでなんとか役に立つものをと思って、ある集計作業を自動化させたところ、今後も暇なときはよろしくということになりました。
いろんなものを作りましたが何をどのように作るかから、その後の運用保守までほぼ一任してもらって大変面白かったです。しかしなにぶん仕様や実装方法について相談できる方がおらず全ては私の泥縄式学習術によって成り立っているという恐ろしい状態でした。体系的な知識や組織の経験知みたいなものが一切ないので自分がどこにいるのか、努力の方向性が合ってるのかも結果が出るまでわからない。先達のあらまほしきことなり。
学歴がよくなくて、就職が困難だったので中小 SIer で働いていた。 (プライム案件を取ってこれる分マシらしい)
レキサルティ、レクサプロ、デパスのお世話になって続けてたけど、結局は薬でどうにかできず、辞めてしまった。
参考程度だけど、未経験の人が 300万 をもらうために、どのようなスキルが必要かを、まとめておく。
ちなみにどれくらいプログラムが書けなかったかというと、競技プログラミングで努力しても AtCoder の黄色になれず青色のままってくらい。
AtCoder でいう、初心者から抜け出せないという、要するにセンスがないということなのだけど、そういう人も居そうなので、参考までに。
未経験のプログラマに対して、これだけ要求されるのだから、未経験の人は覚悟するようにという指針を提供したいので書いた。
基本的に、損害を与えた場合には、それを作業者が補填するという誓約書を結ぶ。
要するに、捨て駒として扱って、失敗したら賠償しろ、という事になる。
このことを認識して、失敗しないように振舞ないと、連帯保証人含めて迷惑をかける事になる。
要するに、低賃金で未経験プログラマを案件にノーリスクで送りこんで、稼ぐための手段です。
基本的に PL (夢想家) → PM (御用聞き) → プログラマ という環境なので、プログラマが自分でディレクションして意思決定する必要がある。
例えば、下請けの場合は、PM の御用聞きの結果の WBS に合わせないと、顧客から DM で 瑕疵担保責任がどうとか言われる。
社内開発の場合は、PL の方から直接、長時間の叱責を受けなくてはならない。
そういう不幸を防ぐためにも、自分でディレクションして、PM の決めた実態を反映していない WBS に合わせて作業するスキルが要求される。
基本的に手戻りは個人の過失になってしまうため、手戻りしないように考え抜いて意思決定をする、というのが重要になる。
これこそ、ガクチカと呼ばれる、頑張れますというスキルなので、学生時代に頑張っておけばよかったなぁ。
こう見せたい、こう表現したい、という事を伝えるには、必然的にデザインの知識が必要になる。
創造的思考とデザインは切っても切り離せない概念で、デザインとは創造なのだから、当たり前である。
ソフトウェアアーキテクチャも、ソフトウェア設計も、コーディングもデザインと言えるかもしれない。
顧客と 1:1 で話す事が DM でもボイチャでも突発的に発生するので、いつ、いかなる時でも論理武装していなければならない。
まぁ、顧客であったり PL であったりはキレるのが仕事なので、それに対して理路整然と説明する必要がある。
なんとなく、では納得しないし、すぐ損害賠償請求とかそういう話にいくので、答えられないと持ち帰りますとお茶を濁して、エマージェンシーになる。
後述する設計能力においても、課題を把握するための言語技術(言語化能力)は重要なファクターだと思う。
C/C++ のシステムプログラムはフレームワークが基本的に無いので、自分で概念を整理して、どのような変更、拡張があるかを考えて設計する必要がある。
この能力が弱いと、手戻りが発生しやすくなり、瑕疵担保責任を問われることになる。
読んだ本の中だと、ボブおじさんの本が、やっぱりしっくりくるなという個人的な感想がある。
UDP で送ってくるデータを受けて 24/365 で停止しない WebAPI への繋ぎ込みという簡単な作業があって、振られた。
リークしてはいけないという事で malloc は禁止で、グローバル変数を利用するという変なルールがあった。
Rust で書けばいいんじゃないかなと思ったけど、Rust 書くのもシンドイし、C/C++ で、しんどくて読みづらいコードを書いた。
あとで保守する人が大変そうだけど、そういうルールを決めたのは PL だしね。
なんか、特殊な PCI Express のカードからベンダーが用意している SDK でデータ引っこ抜いて Web API へつなぎ込む部分をやった。
一応、SDK の使い方をパラ見して 1 日で作ったので、別に負担じゃなかったけど、素人にやらせるんなとは思った。
当たり前だが、DB 作って RestAPI を生やすのは現代のプログラマにとって自然にできなければならない。
なので、新規開発のサブモジュールのバックエンドを任せられた。
だが、ORM の癖を把握したり、発行されるクエリを確認したりするのは、疲れる。 SQL を直書きするのはシンドイ。
結局 SQL を直書きすることにしたけど、あまりいい決断ではなかったと思っている。
それ以外は フレームワーク に乗ってしまっていいので、書き捨てる分には楽だった。
最近だと、TypeScript で Prisma 使うのが、型安全でよさそうだなと思っている。
デプロイを EC2 直でやったり ECS にしたりとしていたので、ベアメタルの知識が必要になった。
要するに systemd のいじり方とか、死活監視の仕方とか。
個人的には、クラウド嫌いなので、ベアメタルの方が安心できる。
Bind で権威DNS を管理して、postfix で絶対止めてはいけないメールサーバを管理するとかもあったけど、出来て当然ではある事だし。
未経験プログラマでも、月単価 100 万以上で顧客に請求してるんだから、会社はそりゃ儲けるだろうと思った。
会社が一人前の経験N年のプログラマといったら、その通りに振舞う必要がある。顧客に責任はないのだから。
当たり前だが、Webディレクション、Webデザイン、Webプログラミング, Webマークアップ は、全て作業者であるプログラマの仕事になる。
個人的には、これが分かれている理由が良く分からないけど、分けたい人がいるんだろう。
デザインで、CSSフレームワークを使うと、その色が出るという事で、全部 CSS は手書きしていた。
tailwind が出た現在では使っていればよかったなと思う。
結局、全く分からない中、手探りでデザインし、コードを書いて、顧客に 1 日 5 ~ 10 回リリースするという行為をした。
顧客は大手企業だったので、自社のエンジニアならもっと出来る、と叱責されまくったけど、だったら自社でやればいいじゃんと思った。
一応、今でもサービスは生きていて、ユニークユーザ数は上がっているらしい。
そして、焼き付け刃だったので、 WAI-ARIA を知らず、アクセシビリティへの配慮が足りない事が問題になってしまった。
これはなんとか保守対応にねじ込めたのでトラブルにならなかったけど、瑕疵担保責任と綱渡りだなと思った。
当たり前だが、リリースサイクルを短くしないと顧客はキレてしまうので、CI/CD を整えないといけない。
今は Github Actions とかあるけど、昔は無くて Bitrise が高いからみたいな理由で Azure Pipelines で CI/CD フローを構築した。
もう Multi Stage Pipeline になってるだろうけど、Release Pipeline が GUI からしか設定できないのが辛みだった。
当然だが、デプロイするためには IaC を整える必要がある。
これを知らずに、コンソールでポチポチしていたので、 IaC 出来てない事がバレた時に色々怒られてしまった。
本来はテストも自動テストを整えて、質保証をしてバグを減らさなければならない。
だが、テストを書くという手間を払えなかったので、人力テストしかできなかった。
一応、リグレッションテストを人力でやりまくったので、バグ発見曲線が結合テストでの IF 不一致しかない、という結果にはなったけど
自動化できれば費用が必要じゃなかったから、怠慢だと、責められてしまった。
未経験でも誓約書を盾に、振られた事全部を出来なくてはならない慣習があるので、プログラマはそんなに良い職業じゃないよ。
甘い考えで、プログラマになろうと思っているのなら、考え直した方がいいです。
もうほとんど美的センスみたいなもので、センスのないやつは美しいプログラムや醜いプログラムに反応する嗅覚がない
何の疑問も持たずハードコーディングし、グローバル変数を定義し、要領の得ない長大な関数を書いてしまう
コーディングルール以前にセンスのあるプログラマなら、醜くて自然と顔をしかめてしまうようなプログラムも
コーディングルールもどうでもいい些末なことに拘るオタクのイチャモンとしか思えないらしい
何が美しいプログラムで、何が醜いプログラムか判定する感覚が欠如している
午後Ⅰをなめすぎた。午後ⅡはOKな気がするが午後1で死んでそう。
ひたすらタスク制御の理解が早く正確になるように努力すればよさそう。
■午前2
イアエウイ イエウイウ ウイエイア イウウエア イウイウア
#2,7,11,13は全然わからん。過去10年にあったのは半分ほど。
後で調べて…
#21は大域的データ=グローバル変数。答えはイであってそう。
#24はアが正解そう。VRとARとMRを混同していた。ウはVR。✕
#25はアで合っている。ウのNBはNallowBandの略。
22/25 で合格。
<大問1>・・・丁寧にやってて55分もかかってしまった。。。
設問1
(1)紙幣の裏表と向きの鑑別結果
(2)イエ ・・・状態イは、反転①②を両方やれば解消する。しょうもな。
(3)10束 ・・・間違ってそう。60秒/38ms=1578.…枚/分。
設問2
(1)a:束タスク、b:時間計測の中止の通知、c:動作停止、d:タイマタスク、
(2)f:S1での検知、g:S6での検知、h:S7での検知
設問3
(1)i:すべての投入済み通貨が整理処理済であることを確認する。
(複数の通貨を処理するので、処理開始前に別の通貨が処理完了を見届ける。) (2)j:通貨ごとにその紙幣での最大幅から算出した計測時間
設問1
(1)スマホから電源OFFの通知を受ける、異常な加速度を検知する
(2)計測失敗が制御タスクに通知され、走行プランが作成されず、走行を停止する。
設問2
(これでいいのか?スマホに測位情報を表示する必要があるので。)
(2)5
(最大0.6秒後に通知され0.5秒以内で衝突検知<0.25*X、X=5)
(3)42cm
(5.4km/h=150cm/sで進むので0.18sだと27cm。あと15cm進むので27+15=42
(4)優先度が高いため頻繁に停止してしまう。(の旨。)
設問3
(3)測位情報が一定時間変化なしとなった時。(制限時間切で最後まで記入できず。)
■午後2
<大問2>
設問1
(1)
(b)スマートバスzに対して、バス情報通知を送信するように指示
(2)
(3)
(a)180 Mbit/s
(7.2Mbitを40msで伝送すればいよいので180.0Mbit/s)
(b)50 フレーム
(0.5s=10フレームで1アウトで、5アウトチェンジなので50)
(c)40 フレーム
(普通に状態2からさらに居眠りで2秒検知なので40フレーム。単純すぎ?)
設問2
(1)
(a)ドアが開閉したまま走行開始しないように、走行開始前にドアが閉まって
(2)急加速、急減速、蛇行の判定結果
(3)
(a)緊急ボタンが押下された
(b)スピーカ
(緊急停止時にはスピーカから乗客アナウンスする。再開時にはしない。)
設問3
(b)
変更内容:通過と判定した後でも、「降車ボタンによる停車」であれば、
(はじめからそうなってると思う。)
以上
まずMQL4とMQL5ではiMA等のテクニカル指標関係の関数の使い勝手が大幅に変わってる。
例えば期間75のEMAと期間200のEMAの2つのiMAを使うなら、MA075handleとMA200handleみたいにint型変数を用意しなきゃいけないらしい。
EAにしろカスタムインジケーターにしろひとつハンドルを作ったら同プログラム内で使い回すことになるだろうから、グローバルで宣言するのがよろしいっぽいな
MQL4では関数を呼び出したらその都度計算してる訳で、過去の足の値を参照するにも毎度計算してるから重いっつーことで、一度計算した値はバッファ配列に放り込めば過去の値は読み出すだけで済むようになったらしい。
動的配列(すなわち最初のインデックスを数値なしでの宣言、具体例は double MA075buffer[]; という形での宣言)でないとだめで、ArraySetAsSeries関数で時系列並び(具体例は ArraySetAsSeries(MA075buffer,true); )にするべきだろう。
では、移動平均では指標がひとつしかなくて参考になりづらいので、MACD(メイン線とシグナル線)でやってみよう。
double MacdMainBuffer[]; //MACDのメイン線バッファ
double MacdSignalBuffer[]; //MACDのシグナル線バッファ
int OnInit()
{
MacdHandle = iMACD(NULL,0,12,26,9,PRICE_CLOSE); // MacdHandleにiMACDのハンドルが代入される。この時点ではメイン線・シグナル線の区別をつけないことに注目
return(0);
}
void OnTick()
{
CopyBuffer(MacdHandle,MAIN_LINE,0,10,MacdMainBuffer); // 2個目の引数でメイン線・シグナル線を切り替える。MQL4ではMODE_MAINとMODE_SIGNALだったがMQL5では表記が変わった模様
CopyBuffer(MacdHandle,SIGNAL_LINE,0,10,MacdSignalBuffer); //勿論メイン線なら0、シグナル線なら1と入力しても差し支えはないが、僕の場合可読性重視の為に可能な限り定数型で書く
//過去10本の足だけバッファにコピーするようにした。EAならそんなに過去を参照することもないだろうし、ぶっちゃけ10もいらない気もする
ArraySetAsSeries(MacdMainBuffer,true); //時系列並びにするとインデックス番号0が現在の最新足の値になる
ArraySetAsSeries(MacdSignalBuffer,true);
void OnDeinit()
{
}
ってやらないといつまでもメモリを確保したまんまになっちゃうらしい。
MQL4と違って扱いはなにかと面倒くさくはなってるなーという印象