27
27

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

プログラミングスキル向上のための問題集(レベル判定付き!)

Last updated at Posted at 2021-07-22

概要

プログラミングに関する簡単な問題集です。

世の中にはプログラミング言語の解説本や、テクニック集など、スキルを高めるのに有用なリソースが豊富にあります。
ただし、プログラミング学習において次のような意見を聞いたことは一度くらいはあると思います。

本を理解することに努めるよりも手を動かして慣れろ

これについては私も同意見なのですが、PCの前に座って、よーし勉強するぞー!と意気込むのって結構根気が必要じゃないですか?もっと気軽に取り組めて、スキルも上げられる方法があったらいいと思いませんか?

受験勉強を例に挙げると、ある程度基本を身に着けたら後は問題を解いて定着させる、という方法を実施している学生は多いと思います。なぜならそれは記憶に残りやすく効率の良いやり方だからです。ひたすら教科書を読んでインプットをしてもなかなか結果に結びつきにくいわけです。

一方でプログラミングに関していうと、こちらも勉強の一貫でありながら、そういった問題集というものはあまり見かけません。そこで、ないなら自分で作ればいいじゃん!と思い立って書き始めたのが本記事です。私の感想をここで長々と書いてもつまらないので、早速内容に入りたいと思います。

問題集

問題には3段階でレベルをつけています。上級者とかの定義についてはなんとなく決めたもので、特に深い意味はないです。

  • ★☆☆: (入門者~初級者)
  • ★★☆: (初級者~中級者)
  • ★★★: (中級者~上級者)

言語についてはいろいろな人が読めるよう、バラバラにします。Python JavaScript Java C++ VBAあたりを考えています。

ただし、特定言語機能に依存する問題は出題せず、全言語の共通知識となるようなものを出題します。

また、想定している読者は安定稼働が期待される業務システム開発者です。競技プログラミングで使える知識などは対象外となります。

問1

  • レベル: ★☆☆
  • 種別: 保守性

コード

class User:
    def __init__(self, userName, password):
        self.__userName = userName
        if (len(password) >= 8):
            self.__password = password
        else:
            raise ValueError('The password length must be greater than 7.')

def main():
    try:
        user = User('kazupaka', 'StrongP@ssword!')
    except ValueError as e:
        print(e)

main()
解答例
class User:
    def __init__(self, userName, password):
        PASSWORD_MIN_LENGTH = 8
        self.__userName = userName
        if (len(password) >= PASSWORD_MIN_LENGTH):
            self.__password = password
        else:
            raise ValueError('The password length must be greater than %d.' % (PASSWORD_MIN_LENGTH - 1))

def main():
    try:
        user = User('kazupaka', 'StrongP@ssword!')
    except ValueError as e:
        print(e)

main()

コード中の8はマジックナンバーと呼ばれ、意図がわからない"魔法の"数字として忌み嫌われています。
ほぼすべての人が数字の意味を想定できる場合(例えば、三角形の面積公式の底辺×高さ÷2の2など)を除き、変数化して意図を明確にしましょう。

問2

  • レベル: ★★☆
  • 種別: 保守性

コード

if ((department.toUpperCase().indexOf("ENGINEERING") > -1) &&
    (employee.type().toUpperCase().indexOf("LEADER") > -1) &&
    employee.age() > 40) {
    ...
}
解答例
boolean isEngineeringDepartment = department.toUpperCase().indexOf("ENGINEERING") > -1;
boolean isLeader = employee.type().toUpperCase().indexOf("LEADER") > -1;
boolean ageIsMoreThan40 = employee.age() > 40;

if (isEngineeringDepartment && isLeader && ageIsMoreThan40) {
    ...
}

複雑な条件は説明用の一時変数に置き換えます。この例はまだマシですが、もっと複雑な条件になると読むのが苦痛になります。
修正後はコードの意図をつかみやすくなっています。

今回はbooleanに置き換えましたが、状況によってはメソッドに置き換えることもあります。
コメントで説明すれば良いという意見もあると思いますが、基本的には良いコード>普通のコード+良いコメントです。
コメントは保守されず、古くなったりすると嘘をつくこともあります。コードは思ったとおりには動きませんが、書いたとおりには動きます。嘘はつきません。

問3

  • レベル: ★☆☆
  • 種別: コンピュータサイエンスの基礎

コード

// 税込み価格を計算
double computePriceIncludingTax(int32_t price) {
    const int32_t TAX_RATE = 10;
    return price / 100 * (100 + TAX_RATE);
}
解答例
double computePriceIncludingTax(int32_t price) {
    const int32_t TAX_RATE = 10;
    return price * (100 + TAX_RATE) / 100;
}

計算順によって、税込価格がうまく計算されないことがあります。
具体的な値を当てはめてみましょう。例えば3,980円の物品を買うとします。
前者の場合:

  • 3980 / 100 = 39
  • 39 * (100 + 10) = 4290

後者の場合:

  • 3980 * (100+10) = 437800
  • 437800 / 100 = 4378

前者は最初の割り算が整数型で計算され、小数点以下が切り捨てられてしまいました。
小数点計算は切り捨てや誤差で思わぬ結果になることがあるので注意しましょう。

慣れている開発者なら、浮動小数点系の計算がでた時点で警戒レベルが2つくらいは上がるはず。
情報落ち桁落ちなどの用語や浮動小数点計算の誤差というワードにピンと来ない人は復習しましょう。

問4

  • レベル: ★★★
  • 種別: 保守性

コード

const computeArea = (shape) => {
    switch (shape.kind) {
        case "triangle":
        return shape.base * shape.height / 2;
        case "square":
        return shape.width ** 2;
    }
}

const triangle = {
    kind: "triangle",
    base: 3,
    height: 2
};

const square = {
    kind: "square",
    width: 5,
};

const triangleArea = computeArea(triangle);
const squareArea = computeArea(square);
解答例
const computeArea = (shape) => {
        return shape.area();
}

const triangle = {
    base: 3,
    height: 2,
    area: function() {
        return this.base * this.height / 2;
    }
};

const square = {
    width: 5,
    area: function() {
        return this.width ** 2;
    }
};

let triangleArea = computeArea(triangle);
let squareArea = computeArea(square);

ポリモフィズムに関する問題です。オブジェクト思考に染まっている人なら、switch文を見かけたら、switchをなくせないか考えるはず。

今回はswitchの分岐が2パターンですが、追加開発をすると分岐が増えるというケースはよくあります。
分岐ごとに複雑な処理があったりすると、コードがごちゃごちゃになりますね。

そこで使えるのがポリモフィズムです。
switchの分岐で処理をするのではなく、オブジェクトに計算関数をもたせ、それを呼ぶ形式にします。
こうすることで呼ぶ側は共通処理となり、分岐が排除できているのがわかると思います。

オブジェクト指向については問題集では書ききれない分量となります。
すでにQiitaにはわかりやすい記事がたくさんあるので、そちらを参照することをおすすめします。

今回の出題は以上です。

感想

書きたいと思っているポイントはたくさんあったのですが、いざ問題にするのは思いの外難しかったです。
ちょっと難しい問題になると問題も解説も長くなってしまい、サクッとできないレベルになるためいくつかボツにしました。
モチベが続けば第二弾を書こうと思います。

27
27
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
27
27

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?