関数 · 14
関数
処理に名前を付けてまとめ、値を受け取って結果を返します。再帰も。
関数は名前の付いたコードのまとまりです。
値(引数)を受け取り、結果を返します。
下の例は、2乗を求める関数と、自分自身を呼ぶ階乗の関数を作って使います。
def square(x): return x * x def factorial(n): if n <= 1: return 1 return n * factorial(n - 1) n = int(input())print(square(n))print(factorial(n))function square(x) { return x * x;} function factorial(n) { if (n <= 1) return 1; return n * factorial(n - 1);} const n = Number(require("fs").readFileSync(0, "utf8"));console.log(square(n));console.log(factorial(n));import { readFileSync } from "node:fs"; function square(x: number): number { return x * x;} function factorial(n: number): number { if (n <= 1) return 1; return n * factorial(n - 1);} const n: number = Number(readFileSync(0, "utf8"));console.log(square(n));console.log(factorial(n));import java.util.Scanner; public class Main { static int square(int x) { return x * x; } static long factorial(int n) { if (n <= 1) return 1; return n * factorial(n - 1); } public static void main(String[] args) { int n = new Scanner(System.in).nextInt(); System.out.println(square(n)); System.out.println(factorial(n)); }}using System; class Program{ static int Square(int x) => x * x; static long Factorial(int n) { if (n <= 1) return 1; return n * Factorial(n - 1); } static void Main() { int n = int.Parse(Console.ReadLine()); Console.WriteLine(Square(n)); Console.WriteLine(Factorial(n)); }}#include <iostream> int square(int x) { return x * x;} long long factorial(int n) { if (n <= 1) return 1; return n * factorial(n - 1);} int main() { int n; std::cin >> n; std::cout << square(n) << '\n'; std::cout << factorial(n) << '\n';}#include <stdio.h> int square(int x) { return x * x;} long long factorial(int n) { if (n <= 1) return 1; return n * factorial(n - 1);} int main(void) { int n; scanf("%d", &n); printf("%d\n", square(n)); printf("%lld\n", factorial(n)); return 0;}square(x){ 返す x * x;} factorial(n){ もし (n <= 1) { 返す 1; } 返す n * factorial(n - 1);} n = コンソール.行読み().数に();コンソール.書く(square(n));コンソール.書く(factorial(n));4
16 24
ポイント
- 同じ計算をあちこちで使うときや、長い解答を小さな部品に分けるときに関数にまとめます。
returnに来ると関数はその場で終わります。
その下の行は実行されません。- 数を渡すと値がコピーされます。
関数の中でx = 9と変えても、呼び出し側の変数はそのままです。 - リストを渡すと同じリストを共有します。
関数の中で追加すると外でも変わります(C++ のvectorは渡すときにコピーされます)。 - 再帰は関数が自分自身を呼ぶことです。
大きな問題を同じ形の小さな問題に縮め、必ず止まる条件が必要です(n <= 1なら1)。 - 止まる条件がないか深すぎると、言語によってエラーで止まるか、プログラムごと落ちます。
言語ごとの書き方
| Python | def square(x): とインデント。return がなければ None を返します。def が実行されたあとでしか呼べません(先に呼ぶと NameError)。再帰は既定で約1,000段で RecursionError |
|---|---|
| JavaScript | function square(x) { return x * x; } または const square = (x) => x * x;。return がなければ undefined。function 宣言は呼ぶ場所より下にあってもかまいません。深すぎると RangeError: Maximum call stack size exceeded |
| TypeScript | JavaScript に型を付けます:function square(x: number): number。引数の数が違うと型チェックで教えてくれます |
| Java | クラスの中に static int square(int x)。返す値がなければ void。クラスの中ならどこに置いてもかまいません。 深すぎると StackOverflowError |
| C# | Java と同じくクラスの中に static int Square(int x)、返さないなら void。深すぎる再帰はプログラムごと落ちます |
| C++ | int square(int x) のように返す型を前に書き、返さないなら void。使う前に上で定義するか、 int square(int x); で宣言します。深すぎる再帰はプログラムが落ちます |
| C | C++ と同じです |
| Craykon | キーワードなしで square(x) { 返す x * x; }。呼ぶ場所より下にあってもかまいません。 数千段を超えると「関数を深く呼びすぎて止まりました」 |
よくある間違い
止まる条件がない
def factorial(n):
return n * factorial(n - 1)→ 自分を果てしなく呼び、RecursionError で止まります。ほかの言語もスタックがあふれて止まるか落ちます
先頭に if n <= 1: return 1 のような止まる条件を置きます。
引数を渡し忘れたJavaScript
function square(x) { return x * x; }
console.log(square());→ NaN — 足りない x がエラーなしで undefined で埋められます
値の数を合わせます。
Python(TypeError)、Java・C#・C・C++・TypeScript(コンパイルエラー)、Craykon(「値が2個必要なのに1個しか渡していません」)は教えてくれます。
関数の中で変えたのに元が変わらないC++
void add(std::vector<int> v) { v.push_back(9); }
std::vector<int> a = {1};
add(a);→ a は {1} のまま — コピーに追加しました
元を変えるには std::vector<int>& v のように & を付けます。
言語ごとの違い
階乗はすぐ大きくなります。
32ビットの
階乗はすぐ大きくなります。
32ビットの
int には 12! までしか入らないので(Java で int を使って 13! を計算するとエラーなしで 1932053504)、Java・C#・C・C++ の例は64ビットの long/long long(20! まで)を返し、Python には大きさの制限がなく、JavaScript・Craykon は 2^53 を超えると正確でなくなることがあります。