みなさんはプログラミングは得意ですか。わざわざこのような記事を見ているということは、もしかしたら得意なかたかもしれませんね。ただ、何年もプログラミングを仕事や研究で経験されていないと「得意でない」「わからない」という場合がほとんどではないでしょうか。

本連載ではそのようなプログラミングを得意としていない人を対象に、Pythonと呼ばれるプログラミング言語を使ってプログラミングの概念や文法について学んでもらいたいと考えています。そこで、Pythonの文法について教科書のように詳細に記載するというよりも、「プログラミングの普遍的な概念を理解したうえで、Pythonのコードを実際に書く」という実践的なスタイルで解説していきます。最終的には、Python以外の、CやJavaといった言語を学びたい方でも有用なコンテンツとなるよう、心がけていきます。

なお、本連載はシスコシステムズ Japanの社内で行われた4日間のPython Programmingにもとづいて作成されています。ご興味があるかたはオリジナルの資料もご参照ください。

プログラミングって何?

機械は人間の言葉をそのまま理解することはできません。0、1のバイナリで書かれた命令をメモリに読み込み、CPUがそれを実行することで機械は人間の命令を実現します。

たとえば、PCのディスプレイに"Hello"と表示させたいとします。これを「機械が理解できるように0、1で命令して、実行させろ」といわれても、途方にくれてしまいますよね。0、1の命令にも規則性があるので、これぐらいの命令であれば書けないこともないですが、"001100011000111……" などというように、0、1を羅列した命令を書けといわれてもノイローゼになってしまいそうです。

プログラミングはこの「0、1で命令を与える」という難しい作業を人間がより簡単に行うための方法です。ざっくりいってしまうと、人間と機械の両方にとってわかりやすい言葉で「機械が解釈できる命令を書く作業」がプログラミングといえます。

以下にこの概念を示す図を記載します。

プログラミングの概念

図の真ん中に位置する「C、Java、Pythonといったプログラミング言語」は人間と機械の中間に位置する言葉であり、人間もそれを解釈することができますし、機械もそれを0、1として認識可能です。そのため、人間がプログラミング言語で機械にやらせたい命令を書くことで、それを機械が実行する、というスタイルで機械に作業をさせることができます。

"Hello"を表示するために「0、1を延々と羅列する」のと「print("Hello")」と書くのを比べたら、どう考えても後者のほうがずっと簡単で効率的ですよね。だから、みんなプログラミング言語を使います。

※広義では0、1で命令を羅列することもプログラミングといえます。ただ、今ではあまり一般的ではないと考えています。

Pythonと他のプログラミング言語の比較

プログラミングを実現する手法や言語はさまざまです。プログラミング言語にはそれぞれ得意な分野があり、書きたい作業によって、使いたい言語を選びます。今回学ぶPythonは、そのようなプログラミング言語のひとつであり、他の言語よりも比較的汎用的(なんにでも使える)であり簡単です。そのため、今回のプログラミング入門の連載にはPythonを題材として使うことにしました。

Python以外にもいいプログラミング言語はたくさんありますが、なぜプログラミングを学ぶのにPythonがよいのでしょうか。

これにはいくつもの理由がありますが、まずは論より証拠を見せたほうがはやいと思いますので、「ディレクトリ(フォルダ)の階層を書き出すプログラムを作る」ということを複数の言語で行い、それを比較してみます。

まずは「ディレクトリ(フォルダ)の階層を書き出す」方法について考えます。実現方法はいろいろありますが、今回は以下のルールに従って動かすことで実現させます。

  1. ディレクトリの中身を順に見ていく
  2. それがファイルでありディレクトリでなければ、そのまま表示。ディレクトリであれば、インデント(字下げ)してディレクトリ名を[]付きで表示し1.に戻る
  3. ディレクトリ内のファイルをすべてチェックしたら終えたら終了

たとえば、以下のようなディレクトリ構造の場合、

/python --+-- a(dir) --+-- aa
          |            |
          |            +-- ab(dir) ----- aba
          |
          +-- b
          |
          +-- c
          |
          +-- d(dir) --+-- da
                       |
                       +-- db

プログラムでこれを表示させると次のようになります。

[/python]
    [/python/a]
    - aa
        [/python/a/ab]
        - aba
- b
- c
    [/python/d]
    - da
    - db

上記のルールにしたがって、この構造のディレクトリを表示させた際のプログラムの動きを具体的に書きくだすと、以下のようになります。

  1. pythonディレクトリの中を確認
  2. aはディレクトリなのでインデントしてディレクトリを表示
  3. aディレクトリの中を確認
  4. aaはファイルなので表示
  5. abはディレクトリなので、更にインデントしてディレクトリ名を表示
  6. abディレクトリの中を確認
  7. abaはファイルなので表示
  8. abディレクトリの中身を確認し終えたので、aディレクトリの確認の続きに戻る
  9. aディレクトリの中身を確認し終えたので、pythonディレクトリの中の確認の続きに戻る
  10. bはファイルなので表示
  11. 以下省略

ルールに従って規則的に動いていることがわかりますね。この自分で書いた「プログラムの挙動のルール」をアルゴリズムと呼びます。

では、実際にこれを実現するプログラムの例を、いくつかの言語で記載します。各言語について知らない方も多いと思うので、プログラムが実際に何をしているかということよりも、ぼーっと眺めることで、各プログラミング言語の違いについていろいろと思いを馳せていただけたらよいと思います。

なお、比較を簡単にするために、必ずしも教科書的な書き方をしていないので注意して下さい。

Python

まず最初にPythonのプログラムを以下に記載します。 全部で約15行ほどとなっています。

import os

def listFile(path, indentLevel):
    print('  ' * indentLevel + '[' + path + ']');       # ディレクトリ名を表示

    for fileName in os.listdir(path):                   # ディレクトリ内のファイルとディレクトリを全てループで確認
        if(fileName.startswith(".")): continue

        absFilePath = path + '/' + fileName
        if(os.path.isdir(absFilePath)): 
            listFile(absFilePath, indentLevel + 1)      # ディレクトリだったので、そのディレクトリをチェックする
        else:
            print('  ' * indentLevel + "- " + fileName) # ファイルだったので、ファイル名を表示
        
listFile('/python', 0)

コード中にある「#」以降がプログラムのコメントであり、どのような処理をしているかメモしています。重要なのはメモに書かれている「やりたいこと」を少ない行数で実現できていることです。またプログラムを実行することも「コマンドを叩く」だけですぐに実現できるので簡単です。詳しいことは次回以降に扱います。

なお、コメントは外国人でも読めるように英語で書くのが望ましいです。中級以上のレベルになると日本語のドキュメントがない場合もでてくるので、英語はできるに越したことはないです。

Java

Javaで同じことをするプログラムを書くと、以下のようになります。Pythonだと15行程度だったプログラムが、だいたい30行程になっています。

import java.io.File;

public class ListFile {  // クラス定義

    public static void main(String[] args){      // エントリーポイント(プログラムの起点)
        ListFile lf = new ListFile();
        lf.listFile("/python", 0);
    }
    
    public void printIndent(int indentLevel){    // インデントのためのユーティリティ関数
        for(int i=0; i<indentLevel; i++){
            System.out.print("  ");
        }
    }
    
    public void listFile(String dirPath, int indentLevel){
        printIndent(indentLevel);
        System.out.printf("[%s]\n", dirPath);            // ディレクトリ名を表示
        
        File dir = new File(dirPath);
        File files[] = dir.listFiles();
        for(int i=0; i<files.length; i++){               // ディレクトリ内のファイルとディレクトリを全て確認
            if(files[i].getName().startsWith(".")) continue;

            if(files[i].isDirectory()){
                listFile(files[i].getAbsolutePath(), indentLevel + 1); // ディレクトリだったので、そのディレクトリをチェックする
            }else{
                printIndent(indentLevel);
                System.out.printf("- %s\n", files[i].getName());       // ファイルだったので、ファイル名を表示
            }
        }
    }
}

"public void listFile(String dirPath, int indentLevel){"というのが真ん中にありますが、これがディレクトリを走査するアルゴリズムです。Pythonと若干異なりますが、やっていることはほとんど同じです。

プログラムに詳しい人が見ると、増えたのはJavaによる言語の制約(Classの宣言が必要なことなど)や、文字列処理あたりにあることがわかります。メインの処理である"public void listFile" の下にある処理は、Pythonとさほど変わっていません。実行方法に関しても、Pythonよりも難しくなり、まず「コンパイル」と呼ばれる作業で上記のテキストで書かれたプログラムを0、1のバイナリにしてあげて、それをコマンドで指定して実行するという方式になります。

C

最後にCのプログラムです。これは約50行となっています。どうです、もう読む(眺める)のが面倒くさくなってきたのではないですか。書くのはもっと面倒でした(笑)。

面倒なだけではなく、CはPythonやJavaと違い、プログラムが「実行するOS」に強く依存するという問題があります。下記のコードはMacで書いたものなので、Windowsでは動かないのではないでしょうか。

#include <stdio.h>
#include <unistd.h>
#include <dirent.h>
#include <sys/stat.h>
#include <string.h>

void printIndent(int indentLevel);
void listFile(char path[], int indentLevel);

int main(int argc, const char * argv[]) {  // エントリーポイント(プログラムの起点)
    listFile("/python", 0);
    return 0;
}

void printIndent(int indentLevel){         // インデントのためのユーティリティ関数
    int i;
    for(i=0; i<indentLevel; i++){
        printf("    ");
    }
}

void listFile(char dirPath[], int indentLevel){
    DIR *dir;
    struct dirent *dp;
    int i=0;
    struct stat stat_buf;
    
    printIndent(indentLevel);
    printf("[%s]\n", dirPath);  // ディレクトリ名を表示

    dir = opendir(dirPath);
    for(i = 0; NULL != (dp=readdir(dir)); i++){  // ディレクトリ内のファイルとディレクトリを全て確認
        const char* fileName = dp->d_name;
        if('.' == fileName[0]) continue;
        
        char cp[512];
        strcpy(cp, dirPath);
        strcat(cp, "/");
        strcat(cp, fileName);
        stat(cp, &stat_buf);
        if(S_ISDIR(stat_buf.st_mode)){
            listFile(cp, indentLevel + 1);  // ディレクトリだったので、そのディレクトリをチェックする
        }else{
            printIndent(indentLevel);
            printf("- %s\n", fileName);     // ファイルだったので、ファイル名を表示
        }
    }
    closedir(dir);
}

"void listFile(char dirPath[], int indentLevel){" の下にあるプログラムがディレクトリを走査するプログラムです。先に提示したPythonやJavaと同じアルゴリズムですが、ずいぶんと長くなっていますね。

Aという処理を、Pythonだと2行で実現できるのに、Cだと5行必要というような具合で、処理を書けば書くほどプログラムの文量がどんどん増えていくので、CのコードはPythonの3倍以上のコード行数が必要となってしまっています。

また、Cをわかる人が見ればすぐに見抜かれてしまいますが、上記は非常に問題の多いコードです。たとえばディレクトリが深くなったり、ファイル名が長かったりすると一気に破綻してしまいます。本来であれば、そのあたりも考慮したコードを書かなければいけないはずです。

ディレクトリ構造を書き出すという処理をさせるのであれば、あまりCは使いたくないという感想を持っていただけたのではないでしょうか。

最後に、Python、Java、Cの特徴を比較した図を記載します。

Python、Java、Cの特徴

Pythonでプログラミングを学ぶ理由

先ほどの例でわかっていただけたように、PythonはCやJavaに比べると、同じ処理を短いプログラムで実現することができます。短いということをいいかえると、PythonはCやJavaより「簡単」な言語であるともいえます。

私の個人的な意見なのですが、初心者がまず学ぶべきことは、プログラミング言語の仕様詳細よりも、そのプログラミング作業を行う際の考え方や思想だと思います。複雑な処理を実現したいときに「処理を小さい単位に分解して、どのようなステップで実現するか順序立てて考える」ことが必ず必要となります。それを学ぶのであれば、Cのように余計な作法に振り回されて本質に注力できないよりも、Pythonのような簡単な言語で「プログラミングの本質」に着目するほうがいいですよね。

だから私は、プログラミング初心者にはPythonからはじめることを推奨しています(次点はJavaですかね。オブジェクト指向を本格的に学ぶのであれば、Javaのほうがいいかもしれません)。

余談ですが、Pythonは簡単なだけではなく、OpenStackをはじめとした多くの大規模なソフトウェアプロジェクトで採用されていたり、GoogleやCiscoといったメジャーなIT企業でも積極的に利用されています。「エキスパートたちがわざわざ選ぶのだから間違いない」といつも宣伝しています(笑)。

連載のゴール地点

最後に、本連載のゴール地点についてお話したいと思います。本連載はプログラミングのスキルについて「1を10にする」というよりも、「0を1にする」ことを目的としています。

プログラミングは建築にたとえることができます。たとえば犬小屋を作ろうと思ったとき、木の柱や板を調達して適当にデザインして組み立てることができますよね。構造力学の計算をするどころか、建築についてド素人であっても作れます。ただ、「犬小屋とはこういうものだ」ということを知っていて、ノコギリとハンマーの使い方も知っている必要があります。

プログラミングもこれと同じで、比較的「小さい」プログラムはノコギリやハンマーに相当する最低限の文法と「作りたいものの仕組みをどう実現するか」ということさえ知っていれば、そこそこ動くものを作ることができます。

しかし、人が住む家を作るとなると、犬小屋とは話が変わってきます。頑丈であり住みやすい家を作るには、まず「家というものの仕組み」を、犬小屋よりも深いレベルで知っている必要があり、なおかつ家の工作に必要となるスキルは犬小屋より高度なものとなります。ビルの建設などになってくると、さらに高度な知識が必要になってきます。

プログラミングも、単に文法や「言語や設計の思想」だけでなく周辺知識のようなものを身につけていないと、数千、数万行レベルのコードを1人で書いたり、プロジェクトを回すといったことは難しいと思います。より大きく複雑なものを作る場合ほど、より深いレベルの知識が必要になってきます。

本連載のゴールは「犬小屋」を作れるようになること

まずは犬小屋を作るために、ハンマーやノコギリの使い方を覚える。それが本連載の目的です。ハンマーやノコギリの使い方を座学だけで学んで、「私はハンマーとノコギリを使えます」といったら、大工さんに笑われてしまいますよね。使い方は教科書でも学べますが、使いこなそうと思うと、実際に木を切って、釘を打たないと、そのスキルは身につきません。使ってみてはじめてわかることも多いはずです。本連載では演習を載せるようにしていきますので、ぜひ自分で頭を使い、手を動かしながらプログラミングを体感してみてください。

次回は PythonをWindowsとMacにインストールし、その利用方法を紹介します。次回以降もよろしくお願いします。

執筆者紹介

伊藤裕一(ITO Yuichi)

シスコシステムズでの業務と大学での研究活動でコンピュータネットワークに6年関わる。専門はL2/L3 Switching とデータセンター関連技術およびSDN。TACとしてシスコ顧客のテクニカルサポート業務に従事。社内向けのソフトウェア関連のトレーニングおよびデータセンタとSDN関係の外部講演なども行う。

もともと仮想ネットワーク関連技術の研究開発に従事していたこともあり、ネットワークだけでなくプログラミングやLinux関連技術にも精通。Cisco社内外向けのトラブルシューティングツールの開発や、趣味で音声合成処理のアプリケーションやサービスを開発。

Cisco CCIE R&S, Red Hat Certified Engineer, Oracle Java Gold,2009年度 IPA 未踏プロジェクト採択

詳細(英語)はこちら