少し前に、仕事を見つけてインタビューを渡すことに関する記事の波がHabrを席巻しました。 雇用主と求職者の両方が何度も声を上げてきました。 しかし、残念ながら、テスト項目のトピックは十分に扱われていませんでした。
結局のところ、テストタスクは「完了」と「未完了」のカテゴリに限定されません。 プロセスを注意深く観察し、最終結果を検討することで、その人と1日働かなくても、その人について多くのことを言うことができます。 そして時々、新しいことを学びます。
私が働いている会社で長い間候補者に与えてきたテストタスクに注目します。
画面上に、色付きの正方形のグリッドMによるNがあります。 このグリッドで次の効果を実現する必要があります-速度Vで左から右にクリックすると、波が流れ、正方形の色が別の色に変わります(波全体に共通)。 この効果は、M、N、Vのどの値でも機能するはずです。波は常に左壁から始まります。 異なる色のいくつかの波が同時に進むことができます。
アニメーションの例: http : //dl.dropbox.com/u/3601116/wave.swf (フラッシュドライブをクリック)。

この作業は、Habrを訪れるすべてのプログラマーによって簡単に実行されることは間違いありません。
そして、私は次の統計を得ました:
- その結果、20人強の人がタスクを引き受けました。
- 数人は何もしませんでした。
- 残りの半分(私の基準によれば)はそれに対処できませんでした。
- 候補者は明らかに非常に興味深いグループに分けられました。
このタスクをどのように完了するかについて少し考えて、猫の下に行きましょう。ここですべてについて詳しく説明します。
免責事項
この記事では、テストタスクの倫理、複雑さ、および必要性については触れません。ここの全員がこのトピックについて独自の意見を持っていることを理解しています。 コメントの中でそれを素材の追加として聞いてうれしいですが、ホリバーの燃え上がりは歓迎しません。
タスク
課題について一言。 その人が何を実装しているかは気にしませんでしたが、すぐに2つの条件について話し合いました。
- 走れます
- 午前7時に起床されると、コードを理解できます。
グループ
私が言ったように、候補者は別個の(交差する)グループに分割されました。これについては以下で説明します。
人を特定のグループに単独で参加させることは、良い面と悪い面の両方から、すでに彼について多くを語ることができます。 そして、これは絶対に人がすぐに十字架につけられることを意味するものではありません。 グループに参加することは、候補者が私に適しているかどうか、そして私たちが彼に適しているかどうかを調べるために、さらなるコミュニケーションのためのトピックを設定するだけです。
シャズはすべてになります! 明日やるよ!
もちろん、何人かの人々は熱心に戦いに突進しました...しかし、彼らは何もしませんでした。 すぐに消えたものについては、私はすぐに得点しましたが、2、3人の人々は頑固に朝食を食べて、最終的にどこかで合併しました。
結論: オフィスでもフリーランスでも働く価値のない人は誰なのかわかります。 私の時間も候補者の時間も無駄にしないように、手紙を書いてそれ以上のコミュニケーションを拒否することはかなり可能だった。 実践が示すように、市場は実際には小さいです。それは簡単です、あなたはこのようにし、このようにする必要があります
驚いたことに、私はこのタスクの実行方法、アーキテクチャの構築方法、レンダリングの最適化方法を長い間(そして膨大に)説明してくれた人々に出会いました。 技術用語が注がれ、win32 APIの知識が表示されました。 結局、私はそれを我慢できず、率直に言って、彼らは言った、素晴らしい、今行ってこの仕事をしなさい。
そして、あなたは何を知っていますか?
結論: 実際には、理論と実践は非常に異なっています。 多くの場合、人々は彼らの周りにプロ意識の薄い殻を作ります。それは明白なアマチュアリズムを隠します。 時間内に理解して使用するのは良いことです。私たちは2年目にC大学で教えられました
IMPERATIVEは、このグループのドアに大きな黄色い文字で書かれています。 大学の2年目から問題を解決するために予想されるように、マトリックスは、マトリックス、波の始まりと終わりの変数の数、および変数i、j、k、m、nなどの膨大な量の変数を特徴としていました。
コードは理解できず、ほとんどの場合、ジャムおよび/またはゆっくりと動作しました。
だから、私はゼロから波の伝播点までで、jとkは両方向に対称です...そしてこれは境界条件です...そして、どのようなmが現在の色の境界に戻っているのですか...それで、なぜそれがどのように機能しますか?? ! ああ! 性交!
候補者との対話は数十文字遅れており、新しい文字はそれぞれ「すべてを修正し、今では機能する」という言葉で始まりました。 私はコンピューターサイエンスの先生のように感じました。 言うまでもなく、このグループの終わりからプロジェクトを完了した人はいません。 ロシアの教育にはこれで終わりです。
結論: 残念ながら、過熱したIT市場では、ほとんどの候補者は非常に平凡な知識を持ち、すぐに多くのお金とBMWを必要とします。 テストタスク(実際に完了した場合でも)は、人がコードの記述にどのように慣れているか、およびアルゴリズムの問題を解決するための彼のアプローチについて多くを語ることができます。追加(新しいWave());
基本的に、人々は、もちろん、文言のすぐ中で、タスクをクラスと基本アーキテクチャに分割しました-誰が誰を制御します。 私たちは波を作成し、それらに既に何かをさせます。
同時に、彼らはなんとか脳のOOPの奇跡も見せることができました。 しかし、それにもかかわらず、大半はタスクに対処しました。
前のグループに入らないように一生懸命努力した人が何人かいましたが、それでも彼らは場所に立つことができず、教科書のOOPコードに不当にカプセル化されていました。
結論: 標準に従って構築され た 優れたOOPコードは、タスクをモジュールに適切に分解し、それらからアプリケーションを組み立てることができる経験豊富な開発者のサインです。 オーバーエンジニアリングと抽象化違反の兆候に注意を払う必要があります。私の作品の特異性は、建築の抽象化に深く入り込む余裕がない厳しい締め切りにあります。 同時に、ルールを順守し、ルートに沿って同じレーキを散らさないでください。
すべてをゼロから書きます
この割り当てでは、言語、フレームワーク、およびライブラリの使用に特に制限を設定しませんでした。 しかし、このグループの人々は難解な解決策を絶えず選択し、自分たちのためにインフラストラクチャを作成しました(たとえば、3D空間にメッシュスクリーンを表示する)。 その結果、全員がタスクに対処しました。
結論: 一見したところ、候補者は自分の好きな楽器にゼロから問題の解決策を実装するのに十分な知識があり、アルゴリズムに混乱することはありませんでした。 しかし、すぐに「ここで発明されていない精神」の疑いがあり、人が既存の開発とサードパーティのライブラリを効果的に使用できるとは疑っています。簡単な方法を探していません!
前のグループと大きく重複しているこのグループには、真空内で色付きの波を実行するアプリケーションを開発するためのフレームワークを作成することに既に近づいているすべての人が含まれます。その他の恐ろしい決定。
男は、マルチスレッドの高負荷システムを作成できることを証明しようとしましたか? そのようなテストタスクでは?..
「なぜこのようにしたのですか?」 結局のところ、それははるかに簡単にすることができます。
-なんで? できるよ!
結論: 検討する価値があります。 人は、おそらく自分の分野の専門家であり、広範な知識を持っていますが、そのような問題を解決するためのフレームワークを作成せずに、特定の問題を迅速に解決できますか? 配置できるフレームワークはありますか?すべてを再描画している/すべてを再描画していない
多くの人は、左から右へ互いに重なり合った波の動きのアルゴリズムで、レンダリングを大幅に最適化できることにすぐに気付きました。 しかし、悲しいことに、彼らの多くは自分の墓を掘り、自分のコードに絡み合っていました。
しかし、興味深い点がいくつかありました。 たとえば、OOPの観点から動いている波を追加する代わりに、セルをいつどの色で塗り替えるかを考慮して、3つか4つの方法があります。 実際、波の「追加」は、既存の一時的な色切り替えプログラムを変更しただけです。
結論: 一方で、アプリケーションが正しくかつ迅速に機能する場合、再描画についてはまったく気にしませんでした。 一方、人々はすべてをより最適に行うことができることを理解していました。 しかし、彼は最初に機能しない最適なオプションを作成してから、それを最適化し始めましたか? よくない。 そして、これはあなたに考えさせます。コンパイルしてもどうだろう
何人かの人々は、率直なグラフィックショールで完了したタスクを送りました。 コードを読まなくても、内部の何かが間違っていることを理解できました。 それらに気付かないことは不可能だったので、結論は明白でした:人は気にしません。
ほぼ全員が問題を解決することに同意しましたが、ある友人は私を驚かせました。 ご存知のように、彼は横枠とレンダリング速度を修正する方法を非常によく知っていますが、テストタスクに1時間は費やさないと考えています。
結論: 作業が完了したと確信した時点でのみタスクを終了することが非常に重要です。 確認のために明らかな枠を含むアプリケーションに合格すると(彼らが気付かないことを期待して?)、彼は自分の時間だけでなく、検査官の時間も費やします。 そして、問題を解決する方法を知っているが、それを解決しないのは同志です...-職場で同じように振る舞います。私は彼らがそれをどのように実行するか気にしません
何回か、アプリケーションの起動に問題がありました:
- どこですぐに入手できるか明確でないライブラリが使用されました。
- アプリをコンパイルするためだけに、IDE、SDK、および世界中のすべてをインストールする必要がありました。
- 私は明らかにすべてのプロジェクトファイルを持っていなかったし、なじみのない言語ではそれをいじりたいとは思わなかった。
コードは明確でしたが、結果を確認する方法はありませんでした。 この段階で候補者の一人が魔法のように落ちました...
はい、私自身は記事の冒頭でコードが何に書かれているかは気にしないと書きましたが、それでも私の時間に感謝し、たとえば、コンパイルされたファイル、何行か、それをより良く開く方法を数行...ソースファイルのセットではなく、完全なプロジェクトを送信することは価値があります最後に。 もちろん、あなたの仕事はBrainfuckにすべてを書いて、私をock笑することではありません(以下に提案するように)。 しかし、あなたは私がこの動きに感謝できないことを理解しなければなりません。
結論: また、検査官に対するある種の不愉快な態度。 その結果、追加の通信に多くの時間が費やされました。まとめ
各候補者は、何らかの方法で問題を解決するための独自のアプローチを持っていました。 それを研究した後、あなたはすぐに開発者としての人について多くを言うことができます:
- 彼の経験とプロジェクトは何ですか。
- 彼がいかに抽象的に考えているか。
- 彼は簡単なアルゴリズムを実装できますか。
- 彼は自分でタスクを明確に閉じることができます。
特定のグループに属しているということは、それ自体が人の善悪を意味するものではありません。 私にはいくつかのニーズがあり、あなたには他のニーズがあります。 私はいくつかの資質を探しており、他の人を見て驚いていますが、あなたは確かにあなた自身の選択基準のいくつかを持っています。
いくつかの決定によって私は人類への信仰を失い、他の人からは私の目が四角になりました。 コードを読んで、あたかもその作者の頭を突き抜けているかのように-彼がどのように問題に取り組んだか、どのようにアルゴリズムを構築したか、どのような困難に遭遇したか。 私が送ったそれぞれの解決策の後、私は全体としてタスクを異なって見始めました。
もちろん、最も人気があったのは、Waveクラスのインスタンスを作成し、タイマーで更新し、何らかの基準に達したときにクリーニングするOOPアプローチでした。 しかし、同時に、人々はさまざまな方法で波の伝播にアプローチし、グリッドを再描画しました。
一部のソースコードを使用して、自分で何かを学ぶことができてうれしいです!
解決策
*段落を修正ここで
標準的に正しい決定を添付しなかった場合、ストーリーは不完全になります。
実際、最初はそうではありませんでした。 2ダースの「実証済みの作品」の終わりまでに私は考え始めましたが、この問題をどのように解決しますか...すべてのソリューションの1つの機能は面白かったです-誰もが...投稿の冒頭に記載された文字に続いて、その問題を見て:グリッド、その上に波が駆け抜けて......
波とある程度グリッドを無視して、反対側から問題を見ると、この効果は、いくつかのルールを持つ正方形のセルオートマトンに過ぎないことがわかります。 しかし、どれですか?
確かに、セルが0から無限大までの正の整数を取るようにします。 各ティックセルは、4つの隣接セルの最大値を取ります。 左側の境界セルの1つの値をN + 1(Nはグリッド上のセルの最大値)に設定することにより、希望する波の伝播効果が正確に得られることを確認するのは難しくありません。
for (var i:uint = 1; i < Width-1; i++) {
for (var j:uint = 1; j < Height-1; j++) {
buffer.setPixel(i, j, Math.max(bmp.getPixel(i, j), bmp.getPixel(i-1, j), bmp.getPixel(i, j-1), bmp.getPixel(i, j+1)));
}
}
, . , (
https://gist.github.com/valyard/6084814):
var Size:uint = 10;
var Width:uint = int(stage.stageWidth/Size);
var Height:uint = int(stage.stageHeight/Size);
var bmp:BitmapData = new BitmapData(Width, Height, false, 0x000000);
var buffer:BitmapData = new BitmapData(Width, Height, false, 0x000000);
var display:BitmapData = new BitmapData(Width*Size, Height*Size, false);
var zeroPoint:Point = new Point(0, 0);
var colors:Array = [0xFFFFFF];
addChild(new Bitmap(display));
stage.addEventListener(MouseEvent.CLICK, clickHandler);
stage.addEventListener(Event.ENTER_FRAME, frameHandler);
function clickHandler(e:MouseEvent):void {
addWave((e.localY % display.height)/Size);
}
function addWave(position:uint):void {
if (position == 0) position = 1
else if (position >= Height-1) position = Height-2;
bmp.setPixel(1, position, colors.length);
colors.push(0xFF000000 + int(Math.random()*0xFFFFFF));
}
function frameHandler(e:Event):void {
for (var i:uint = 1; i < Width-1; i++) {
for (var j:uint = 1; j < Height-1; j++) {
buffer.setPixel(i, j, Math.max(bmp.getPixel(i, j), bmp.getPixel(i-1, j), bmp.getPixel(i, j-1), bmp.getPixel(i, j+1)));
}
}
bmp.copyPixels(buffer, buffer.rect, zeroPoint);
var rect:Rectangle = new Rectangle(0, 0, Size, Size);
for (i = 0; i < Width; i++) {
for (j = 0; j < Height; j++) {
rect.x = i*Size;
rect.y = j*Size;
display.fillRect(rect, colors[bmp.getPixel(i,j)]);
}
}
}
, . , .
Update: , , . , , . , . , .
, . - , , , , … … , !
P.S. .