LoginSignup
0
0

More than 1 year has passed since last update.

TypeScriptハンズオン 学習メモ 3.関数をマスターする

Last updated at Posted at 2021-10-13

関数の基本

関数とは?

  • 関数の基本形
function hello(name:string) {
    console.log("Hello, " + name + "!")
}

hello("Taro") // "Hello, Taro!" 
hello("Hanako") // "Hello, Hanako!" 

関数内の変数スコープ

  • var
    • 関数の外で宣言した場合、そのソースコード全体で利用できる。関数内で宣言したものは、関数内のみ。
  • let
    • その変数を宣言した構文内({})のみ。

戻り値について

  • function 関数名(引数):型 { 関数の処理 }
  • 戻り値がない場合は、voidを指定する
function total(max:number):number {
    let num = 0
    for (let i = 0; i <= max; i++) {
        num += i
    }
    return num
}

function printTotal(n:number):void {
    console.log(n + "までの合計:" + total(n))
}
printTotal(10) // "10までの合計:55" 
printTotal(123) // "123までの合計:7626" 

複数の値を返す

  • タプルで返す
  • 定義
    • function 関数名(引数):[ タプル ] { 関数の処理 }
  • 呼び出し
    • let [ 変数1, 変数2, .. ] = function 関数名(引数)
function calcTax(totalPrice:number):[price:number, tax:number] {
    const price = totalPrice / 1.1
    const tax = totalPrice - price
    return [price, tax]
}

function printCalcTax(totalPrice:number):void {
    const [price, tax] = calcTax(totalPrice)
    console.log(totalPrice + "の本体価格:" + price + ", 税額:" + tax)
}
printCalcTax(3980) // "3980の本体価格:3618.181818181818, 税額:361.818181818182" 
printCalcTax(9999) // "9999の本体価格:9090, 税額:909" 

引数に条件型を使う

  • function 関数名(引数1:型1 | 型2, 引数2:型3 | 型4)
function menu(a: "yama" | "umi") {
    console.log(a)
}

menu("yama")
menu("kawa") // エラー

オプション引数

  • function 関数名(引数1, 引数2?, 引数3?, ..)
  • function 関数名(引数1 = 初期値1, 引数2 = 初期値2 ..)
function showWindow(width:number = 100, height:number = 50) {
    console.log(width, height)
}
showWindow(80) // 80, 50
showWindow() // 100, 50

可変長引数

  • function 関数名(...名前:型[])
function printTotal(...n:number[]) {
    let total = 0
    for (let i = 0; i < n.length; i++) {
        total += n[i]
    }
    console.log(total)
}
printTotal(2,3,5,7) // 17

関数をさらに掘り下げる

無名関数について

  • const 定数 = function (引数):戻り値 {処理}

アロー関数

  • const 定数 = (引数):戻り値 => 処理
  • 関数はfunction型の値
  • functionとアロー関数の違いは、アロー関数は変数の代入前(アロー関数定義の前)には使用できないこと。
const f = (name:string):void => {
    console.log("Hello, " + name + "!")
}

f("taro") // "Hello, taro!" 
f("ichiro") // "Hello, ichiro!" 

console.log(typeof(f)) // "function" 

内部関数について

  • 関数内で関数を定義して使う
const f = (n1:number, n2:number):void => {
    const inF = (n:number):void => {
        console.log("answer:" + n)
    }
    const sum = n1 + n2
    inF(sum) // 内部関数を使用する
}

f(1 ,2) // "answer:3" 

引数に関数を使う

  • 引数を指定しない
    • const 定数 = (引数:Function):戻り値の型 => 処理
  • 引数を指定する
    • const 定数 = (引数:(引数:型)=>戻り値の型):戻り値の型 => 処理
  • 下の例だと、(...x:number[])=>numberf の型
const calc = (f:(...x:number[])=>number, ...n:number[]):void => {
    const result = f(...n)
    console.log("answer:" + result)
}

const sum = (...s:number[]) => {
    let r = 0
    s.forEach(l => r += l)
    return r
}
const avg = (...s:number[]) => {
    let r = 0
    s.forEach(l => r += l)
    return r / s.length

}

calc(sum, 1, 2, 3) // "answer:6" 
calc(avg, 1, 2, 3) // "answer:2" 

戻り値に関数を使う

  • f1、f2は関数内に0.1、0.08を保持している
  • (n:number)=>number が戻り値の型
const f = (tax:number):(n:number)=>number => {
    return (n:number) => n * (1 + tax)
}
// 10%を課税する関数を取得
const f1 = f(0.1)
// 8%を課税する関数を取得
const f2 = f(0.08)

console.log(f1(12345)) // 13579.500000000002 
console.log(f2(12345)) // 13332.6 

クロージャについて

  • 現在値countと増分nを関数内に保持している
  • 定義された環境を保ち、その中で動く関数のことを クロージャ という
  • 日本語では 関数閉包 という
const f = (n:number):()=>number => {
    let count:number = 0
    return ():number=> {
        count += n
        return count

    }
}
// 現在値を保持して、1ずつ増える関数を取得
const f1 = f(1)
// 現在値を保持して、2ずつ増える関数を取得
const f2 = f(2)
// 現在値を保持して、3ずつ増える関数を取得
const f3 = f(3)

for (let i = 0; i < 10; i++) {
    console.log(f1(), f2(), f3())
}

関数の高度な機能

エラーと例外

  • try構文
try {
  // エラーが発生するかもしれない処理 
} catch(e) {
  // エラー発生後の処理 
} finally {
  // 常に最後に実行する処理 
}
  • 例外を発生させる
throw Error("エラーです")
  • try構文の例
const f = (arr?:any[]):void => {

    try {
        if (typeof(arr) === "undefined") {
            throw Error("arrは空です")
        }

        for(let i of arr) {
            console.log(i)
        }
    } catch(e) {
        console.log(e)
    } finally {
        console.log("finally!")
    }
}

f(["OK", "NG"]) //  "ok" "ng" "finally!" 
f() // "arrは空です" "finally!"

総称型(ジェネリクス)について

  • function 関数名<T>( 引数 ) : 戻り値
  • Tでなくてもいい
  • 複数の総称型を使うには、<T,U,V>のように書く
function getRnd<T>(values: T[]): T {
    const r = Math.floor(Math.random() * values.length)
    return values[r]
}

const data1 = [0, 2, 4, 6, 8, 10]
const data2 = ["グー", "チョキ", "パー"]
const data3 = [true, false]

for(let i = 0; i< 10; i++) {
    const ret1 = getRnd(data1)
    const ret2 = getRnd(data2)
    const ret3 = getRnd(data3)
    console.log(`${ret1}(${typeof ret1})  ${ret2}(${typeof ret2})  ${ret3}(${typeof ret3})`)
}

// "6(number)  グー(string)  true(boolean)" 
// "8(number)  グー(string)  true(boolean)" 
// "0(number)  パー(string)  false(boolean)" 
// "4(number)  グー(string)  true(boolean)" 
// "8(number)  チョキ(string)  true(boolean)" 
// "0(number)  チョキ(string)  true(boolean)" 
// "8(number)  チョキ(string)  false(boolean)" 
// "6(number)  パー(string)  true(boolean)" 
// "2(number)  グー(string)  false(boolean)" 
// "0(number)  パー(string)  false(boolean)" 

ジェネレーターと遅延評価

  • ジェネレータの作り方
function* 名前( 引数 )
  // ...処理...
  yield 
  • ジェネレータは処理を実行し、yieldがあることろで待ち状態になる。.next()で取り出されると次のyieldまで進んで待ち状態になる。
  • 使い方
    • ジェネレータ = 関数() でジェネレータを取得する
    • ジェネレータ.next() で値を取り出す
function* gen(n: number) {
    for (let i = 0; i < n; i++) {
        yield i
    }
}

const g = gen(5)

console.log(g.next()) // 0
console.log(g.next()) // 1
console.log(g.next()) // 2
console.log(g.next()) // 3
console.log(g.next()) // 4
console.log(g.next()) // undefined

非同期処理とPromise

// dミリ秒待ってから、1~nの合計の数を求める
const promiseFunc = (n: number, d: number): Promise<number> => {
    console.log(`start: ${n}`)
    return new Promise((resultFunc) => {
        let total = 0
        for (let i = 1; i <= n; i++) {
            total += i
        }
        setTimeout(() => {
            // thenの中の関数の関数を呼び出す
            resultFunc(total)
        }, d)
    })
}

// 結果を受け取る関数 Promise<number>なのでnumberの引数を取る
const cb = (total: number) => {
    console.log(`result: ${total}`)
}

// 300ミリ秒待って、1~10の合計を表示する(最後に表示)
promiseFunc(10, 300).then(cb)
promiseFunc(100, 200).then(cb)
promiseFunc(1000, 100).then(cb)
// 最初に表示される(最初に表示)
console.log("do something...")

コンソールプログラムを作ろう

コンソールプログラムの仕組み

  • ターミナルのコマンドから渡される引数の情報は、process.argvから取得でき、テキストの配列になっている
process.argv[0] = nodeコマンドのパス
process.argv[1] = スクリプトのファイルのパス
process.argv[2]以降 = コマンド実行時に渡される引数

数字を素因数分解する

console.log("Node path = " + process.argv[0]);
console.log("script file path = " + process.argv[1]);

// 引数で渡された文字列を、数値の配列にする
const data: number[] = [];
for (let i = 2; i < process.argv.length; i++) {
  data.push(Number(process.argv[i]));
}
console.log(data);

// 画面から入力された数値を順に素因数分解する
for (let item of data) {
  // 素因数分解する
  const res = primeFactor(item);
  // 結果を画面表示する
  console.log(`${item} = ${res}`);
}

// 素因数分解する関数
function primeFactor(a: number): number[] {
  const v: number[] = [];
  let x = a;
  let n = 2;

  while (x > n) {
    // 2, 3, 5, と順に割れるか判定して割れたら、v[]に入れ、新たなxを代入する
    if (x % n == 0) {
      x = x / n;
      v.push(n);
    } else {
      // 割れない場合、2のとき1、3以上の時2を加算する
      n += n == 2 ? 1 : 2;
    }
  }
  // 現在の割る数(n)<=割られた数(x)になった場合、もう割れないので割られた数(x)をv[]に入れる
  v.push(x);
  return v;
}
// [ 10, 100 ]
// 10 = 2,5
// 100 = 2,2,5,5

0
0
0

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
0
0