2021/08/28

TypeScript入門 第3回 関数定義

1)はじめに

前回, 逆ポーランド記法で記述した数式を計算するプログラムを作成しました. しかし, 計算する数式をソースコードに埋め込んであって, 数式を変更するためにはソースコードを編集してコンパイルしなくてはなりません. 使い物にならないので, プログラムとしての体裁を整えていきます.

2)関数の定義

計算する部分を関数として定義し, 計算する数式を引数として与えられるようにします. 関数の戻り値は, 計算した結果とします. 計算の過程を記録するスタックは, 関数の中で定義するローカル変数とします. ソースコードは次のようになりました.

// 配列を逆ポーランド記法で記述した式として計算する.
function calc(inputstring[]) {
    let stacknumber[] = []
    for (let value of input) {
        if (value === '+') {
            let a = stack.pop()
            let b = stack.pop()
            if ('number' === typeof a && 'number' === typeof b) {
                stack.push(a + b)
            } else {
                throw new Error('数字が入ってないよ')
            }
        } else if (value === '*') {
            let a = stack.pop()
            let b = stack.pop()
            if ('number' === typeof a && 'number' === typeof b) {
                stack.push(a * b)
            } else {
                throw new Error('数字が入ってないよ')
            }
        } else {
            let a = parseInt(value)
            stack.push(a)
        }
    }

    if (1 === stack.length) {
        return stack.pop()
    } else {
        throw new Error('スタックの要素数が1ではありません. 式を間違えているかも?')
    }
}

// 計算してみる
console.log(calc(['2''3''+''4''*']))

関数名は「calc」としました. 注意点は次の通りです.

  1. 引数名に続けて引数の型を指定する必要があります. 関数の戻り値の型も指定できますが, コンパイラに型推論させる方が良いようです.
  2. 計算結果をコンソールへ出力するコードは削除しました.
  3. 入力をすべて処理できれば, 計算結果を返します. この時, スタックの要素数が1であることを確認し, 1でない時はエラーを投げています.
  4. 関数定義に続いて, 動作確認用のコードを入れています.

このソースコードをコンパイルし実行すると, 計算結果として「20」を表示しました. 問題なく動作しています.

3)演算子の種類を増やす

今のソースコードでは, 対応している演算子は「+」と「×」の2種類だけです. 少なくとも「-」と「/」は対応しておきたいところです. 「+」や「×」を計算するコードをコピーして編集すれば簡単に対応できますが, 似たようなコードが繰り返し現れるのは気持ちの良いものではありません. そこで, 補助関数を導入して全体を見やすくします. 合わせて, 「-」と「/」にも対応します. ソースコードは次のようになりました.

// 補助関数
function calcAux(stacknumber[], op: (anumberbnumber=> number) {
    let b = stack.pop()
    let a = stack.pop()
    if ('number' === typeof a && 'number' === typeof b) {
        stack.push(op(ab))
    } else {
        throw new Error('数字が入ってないよ')
    }
}

// 配列を逆ポーランド記法で記述した式として計算する.
function calc(inputstring[]) {
    let stacknumber[] = []
    for (let value of input) {
        if (value === '+') {
            calcAux(stack, (ab=> a + b)
        } else if (value === '-') {
            calcAux(stack, (ab=> a - b)
        } else if (value === '*') {
            calcAux(stack, (ab=> a * b)
        } else if (value === '/') {
            calcAux(stack, (ab=> a / b)
        } else {
            let a = parseInt(value)
            stack.push(a)
        }
    }

    if (1 === stack.length) {
        return stack.pop()
        
    } else {
        throw new Error('スタックの要素数が1ではありません. 式を間違えているかも?')
    }
}

// 計算してみる
console.log(calc(['2''3''+''4''*''5''/''2''-']))

補助関数の引数は, スタックと演算子に対応した関数を取れるようにしています. このようなプログラミング技法に慣れていない方は, 関数型プログラミングに関する書籍などで勉強することをお勧めします. 

補助関数の注意点として, スタックからポップした値を変数に割り当てる順序を「a, b」から「b, a」に変更しています. 引き算や割り算では, この順序を間違えると正しい答えは得られません. 補助関数を使うことで, calc関数の見通しが良くなりました. このソースコードをコンパイルし実行すると, 計算結果として「2」を表示しました. 問題なく動作しています.

3)まとめ

この記事では, 前回, 作成したプログラムの構成を変更し関数を定義して, プログラムらしく仕上げました. また, 計算のための補助関数は, 関数そのものを引数として与えられるように定義しました. TypeScriptのように型を厳密にチェックしてくれるプログラミング言語であれば, このようなテクニックを安心して使えます. 

しかし, 計算する式を変更するためにソースコードを編集するのは煩わしくなってきました. 次回の記事では, ソースコードを編集せずに計算する式を変更する方法を試します.

最後に, この記事で使用したNode.jsとTypeScriptのバージョンは次の通りです.

PS C:\TSWork\RPN> node --version
v14.17.5
PS C:\TSWork\RPN> .\node_modules\.bin\tsc --version
Version 4.3.5
PS C:\TSWork\RPN>

2021/08/25

TypeScript入門 第2回 逆ポーランド記法

新しいプログラミング言語の勉強を始める時は, 簡単なプログラムを作成するようにしており, 逆ポーランド記法(Reverse Polish Notation, RPN)で書かれた数式を計算するプログラムを作成することから始めています. 逆ポーランド記法に馴染みのない方もおられると思うので, 逆ポーランド記法の説明から始めます. なお, 記事が長くなるので分割します. 

1)逆ポーランド記法

多くのプログラミング言語では, 算数や数学の授業で習った数式と同じ形式で数式を書きます. 例えば, 2と3の足し算は次のように書きます.

2+3

この式に4を掛けてみます.

(2+3)×4

2+3×4

「2+3」全体に4を掛ける場合と3だけに4を掛ける場合とで計算結果は異なります. 一般に使う数式では, 次の事柄を把握しておかないと計算結果がとんでもないことになります.

  1. +や×などの演算子の優先順位
  2. カッコの有無や入れ子の具合

通常の数式では, +や×などの演算子は数字と数字の間に置きますが, 逆ポーランド記法では演算子を最後に置きます. 例えば, 「2+3」を次のように書きます.

2 3 +

「2に3を足す」と日本語の語順通りに読めます. 同じようにして, 「(2+3)×4」と「2+3×4」も逆ポーランド記法で書いてみます.

2 3 + 4 ×

2 3 4 × +

上の式は「2に3を足して, その結果に4を掛ける」, 下の式は「2に, 3と4を掛けた結果を足す」と読めます. 賢明なる読者の皆様はすでにお気づきの通り, 逆ポーランド記法で書いた式にはカッコが現れていません. また, 演算子の優先順位も考えなくて済みます. 

2)スタック

逆ポーランド記法で記述した式を計算する手順, 即ちアルゴリズムを考えていきたいのですが, その前にスタック(Stack)と呼ばれるデータ構造を確認する必要があります.

スタックは, 机の上に積んだ本をイメージすると分かりやすいと思います. 

スタックに対して行える操作は次の2つです.

  1. プッシュ(Push):積んである本(つまり, スタック)に新しい本を載せる.
  2. ポップ(Pop):スタックの1番上の本を取る.

スタックでは, 一番上に積んである本に対してのみ働きかけられ, 途中の本については何もできません. スタックの一番底にある本を取り出すためには, 1番上にある本から順に取り除く必要があります. 

3)アルゴリズム

スタックを用いれば, 逆ポーランド記法で記述した式を計算するアルゴリズムを簡潔に述べられます. 

  1. 式を構成する数値や演算子といった要素を順番に処理します.
  2. 数値の場合, スタックにプッシュします.
  3. 演算子の場合, スタックから数値を2つポップし, 演算子に応じた計算をした結果をスタックにプッシュします.
  4. 式の終端に到達した場合, スタックには1つだけ数値が残っており, その数値が求める答えです. 
  5. 計算の途中で空になったスタックからポップした場合や, 最後に2つ以上の数値がスタックに残っている場合, 不正な式と判断できます. 

4)試してみる

TypeScriptの配列をスタックとして使うためのpushメソッドとpopメソッドがあります. これらを使ってアルゴリズムを試してみます. 

まず, TypeScriptのプロジェクトを作成します. ディレクトリ名は逆ポーランド記法ということで「RPN」としています. プロジェクトの作成方法については, 前回の記事を参照してください. とりあえず, 関数の定義とかも考えず, 入力となる式もソースコードに埋め込んで, 計算できるものを作ります. でき上ったプログラムは次の通りです.

console.log('逆ポーランド記法')

// 入力となる式
let input = ['2''3''+''4''*']
console.log('入力'input)

// 計算のためのスタック
let stacknumber[] = []
console.log('スタック'stack)

console.log('計算開始')
for (let value of input) {
    console.log('入力値'value)
    if (value === '+') {
        let a = stack.pop()
        let b = stack.pop()
        // let c = a + b
        if ('number' === typeof a && 'number' === typeof b) {
            stack.push(a + b)
        } else {
            throw new Error('数字が入ってないよ')
        }
    } else if (value === '*') {
        let a = stack.pop()
        let b = stack.pop()
        if ('number' === typeof a && 'number' === typeof b) {
            stack.push(a * b)
        } else {
            throw new Error('数字が入ってないよ')
        }
    } else {
        let a = parseInt(value)
        stack.push(a)
    }
    console.log('スタック'stack)
}

console.log('計算終了')
console.log('スタック'stack)

コンパイルして実行してみます.

PS C:\TSWork\RPN> .\node_modules\.bin\tsc
PS C:\TSWork\RPN> node .\dist\index.js
逆ポーランド記法
入力 [ '2', '3', '+', '4', '*' ]
スタック []
計算開始
入力値 2
スタック [ 2 ]
入力値 3
スタック [ 2, 3 ]
入力値 +
スタック [ 5 ]
入力値 4
スタック [ 5, 4 ]
入力値 *
スタック [ 20 ]
計算終了
スタック [ 20 ]
PS C:\TSWork\RPN>

期待通りの結果が得られました.

ソースコードでは, 演算子が入力された場合, スタックから数値をポップしています. この時, スタックが空の場合があるため, ポップした結果がundefinedである可能性があります. そのまま計算しようとすると, TypeScriptに怒られてしまいます.

TypeScriptのコンパイラ報告するエラーは事前に潰さなくてはコンパイルすらできません. TypeScriptでは, このようなコンパイル時のエラーに対応するためのソースコードを丁寧に書いて, コンパイラを納得させてからようやく実行できます. これが煩わしく感じられる人もいると思いますが, リリース後にこのような不具合が見つかることを考えると, コンパイラのご機嫌を取るぐらい何ともありません. 逆に, 慣れてくるとコンパイラのチェックの甘いプログラミング言語は怖くて使えなくなります.

ソースコードはコピペしてもらっても良いのですが, 自分で入力することをお勧めします. 自分で入力することでコンパイラの応答を観察できるので, 理解がより深まります. 

5)まとめ

今回の記事では, 逆ポーランド記法で記述した式を計算するためのアルゴリズムを確認しました. 次回の記事では, 作成したプログラムをそれらしく仕上げていきます.

最後に, この記事で使用したNode.jsとTypeScriptのバージョンは次の通りです.

PS C:\TSWork\RPN> node --version
v14.17.5
PS C:\TSWork\RPN> .\node_modules\.bin\tsc --version
Version 4.3.5
PS C:\TSWork\RPN>

2021/08/22

TypeScript入門 第1回 環境構築

1)はじめに

プログラムを実行する環境がデスクトップPCからスマートフォンに移っています. スマートフォンだけで動作するプログラムもありますが, スマートフォンはインターネットと常時接続できるため, サーバと協調して動作するプログラムも多くあります. スマートフォンが接続するサーバは, 自前で用意したオンプレミスのサーバよりもアマゾンのAWSやマイクロソフトのAzureといったクラウドサービスを利用したサーバが大半でしょう. 

このようにプログラムの開発・実行環境がデスクトップPCやオンプレミスのサーバからクラウド環境に移行しており, プログラマとして今後も仕事を続けていくためには, クラウド環境は避けて通れません. クラウド環境でプログラムを開発するのですから単純に仮想マシンを使うのではなく, AWS LambdaやAzure Functionsといったサービスを組み合わせて作りたいものです. そのために最適なプログラミング言語を選んでおきたいので, いろいろと検索して調べた結果, 型安全性を保証してくれるTypeScriptが良さそうな感じがします. 

15年ぐらい前にWebアプリケーションを開発するためJavaScriptを使って以来, JavaScriptから離れていましたので, TypeScriptはもちろんのこと最新のJavaScript事情やNode.jsについて何の知識もありません. 急ぎ書店で「プログラミングTypeScript」と「ハンズオンNode.js」を買い求め, この2冊を一通り読んでみましたが, 分かったような, 分からないようなモヤモヤした感じです. やはり, 簡単なもので良いのでTypeScriptを使ってプログラムを作らないとそのプログラミング言語を習得できません.

これに続く記事では, 私がTypeScriptに入門するにあたり, 試したこと, 考えたことを書きますので, 参考にしていただけると幸いです. なお, この記事では, TypeScriptでプログラムを開発するための環境を構築し, TypeScriptで書いたプログラムが動作するところまでを説明します.

2)環境構築

TypeScriptでプログラムを作成するために用意したものは次の通りです.

  • Node.js
  • Visual Studio Code

Node.jsはTypeScriptで書いたプログラムをJavaScriptにコンパイルし, 実行するために使います. ブラウザ上でTypeScriptを試せる「TypeScript Playground」もありますが, クラウドでサーバレスを目標にしていますので, Node.jsは必須でしょう. 

Visual Studio Code(以下, VSCodeと書きます)はソースコードを編集するためのテキストエディタです. テキストエディタは他にも色々とありますが, 情報が得られやすいVSCodeを選択しました. 

3)最初のプロジェクト

TypeScriptでプログラムを作成するための準備から始めす. まず, エクスプローラなどを用いて作業用のディレクトリを作成します. ディレクトリ名は「HelloTypeScript」としました. PowerShellを起動し, 作成したディレクトリに移動します. 続けて, 次の4つのコマンドを実行します. 

> npm init -y
> npm i -D typescript
> npm i -D @types/node
> npx tsc -init

1番目はプロジェクトの初期化, 2番目はTypeScriptコンパイラのインストール, 3番目はTypeScriptからNode.jsのAPIを使うための型情報のインストール, 4番目はTypeScriptのための初期化を実行しています. 4番目のコマンドはnpmでなくnpxであることに注意してください. lsコマンドを使ってディレクトリの中身を確認するといくつかのファイルができています. 


VSCodeを使って, tsconfig.jsonファイルを編集します. ここは, 「プログラミングTypeScript」の流儀に従って編集します. 編集結果は次の通りです.

{
  "compilerOptions": {
    "target""es5",                                
    "module""commonjs",                          
    "outDir""./dist",     // コメントを外し, 出力ディレクトリにdistを指定             
    "strict"true,                             
    "esModuleInterop"true,                     
    "skipLibCheck"true,                          
    "forceConsistentCasingInFileNames"true      
  },
  "include": ["src"]        // 追加
}

tsconfig.jsonファイルの内容を見やすくするために, コメント行は削除しました. また, 編集した2行にコメントを付けています. 

tsconfig.jsonファイルの設定内容に合わせて, ディレクトリなどを作成します.

  1. distディレクトリを作成する.
  2. srcディレクトリを作成する.
  3. srcディレクトリ内にindex.tsファイルを作成する.
  4. index.tsファイルに「console.log('Hello TypeScript!')」と入力する.

これらのディレクトリやファイルは, VSCodeの画面左側, エクスプローラを使って作成できます. 作成した結果は次の通りです.


index.tsをコンパイルして実行します. PowerShellで次のコマンドを実行します.

> .\node_modules\.bin\tsc
> node .\dist\index.js

実行結果は次のようになります.


Windows10の設定によっては, tscを実行した時に次のようなエラーが表示されます.


このようなエラーが表示された場合, 

スタート→設定→更新とセキュリティ→開発者向け

の順に画面を開き, PowerShellスクリプトの実行を許可します.


ここまでできれば, TypeScriptで開発するための最低限の環境が整いました. 次回から簡単なプログラムを作成しながら, TypeScriptコンパイラの反応を観察し, 勘所を探っていきます.

ヒューマン・リソース・マシーン 入社41年目−並べ替えよ

目次 1)課題 0を終端とした文字列がいくつか流れてきます。各文字列に対してソート(並べ替え)を行い、小さい順(昇順)に右側へ運んでください。 2)状況の確認 この問題では, 予めコードが入っています. このコードを実行して, 何をするコードなのか確かめます.  左のコンベアから...