関数まわりの注意点

関数の定義については紛らわしい側面があって,微積分やプロットなどを行なう際に問題になることがある. この節で,関連する諸問題について検討してみたい.

Sageで「関数」と呼ばれるべきものを定義する方法は何通りもある:

1. 関数, インデントおよび数え上げ 節で解説されている方法で,Python関数を定義する. こうして定義された関数はプロット可能だが,微分積分演算はできない.

sage: def f(z): return z^2
sage: type(f)
<... 'function'>
sage: f(3)
9
sage: plot(f, 0, 2)
Graphics object consisting of 1 graphics primitive
>>> from sage.all import *
>>> def f(z): return z**Integer(2)
>>> type(f)
<... 'function'>
>>> f(Integer(3))
9
>>> plot(f, Integer(0), Integer(2))
Graphics object consisting of 1 graphics primitive
def f(z): return z^2
type(f)
f(3)
plot(f, 0, 2)

最終行の書法に注目していただきたい. これを plot(f(z), 0, 2) としていたら,エラーになっていたはずである. zf 定義におけるダミー変数であって,定義ブロックの外では未定義になるからだ. むろん f(z) のみを実行してもエラーになる. 以下のようにすると切り抜けられるが,どんな場合でも通用するとは限らないので要注意だ(下の第4項を参照).

sage: var('z')   # zを変数として定義
z
sage: f(z)
z^2
sage: plot(f(z), 0, 2)
Graphics object consisting of 1 graphics primitive
>>> from sage.all import *
>>> var('z')   # zを変数として定義
z
>>> f(z)
z^2
>>> plot(f(z), Integer(0), Integer(2))
Graphics object consisting of 1 graphics primitive
var('z')   # zを変数として定義
f(z)
plot(f(z), 0, 2)

こうすると f(z) はシンボリック表現になる.シンボリック表現については,次の項目で解説する.

2. 「呼び出し可能シンボリック表現」(callable symbolic expression)を定義する. これはプロットおよび微分積分演算が可能である.

sage: g(x) = x^2
sage: g        # gはxをx^2に送る
x |--> x^2
sage: g(3)
9
sage: Dg = g.derivative(); Dg
x |--> 2*x
sage: Dg(3)
6
sage: type(g)
<class 'sage.symbolic.expression.Expression'>
sage: plot(g, 0, 2)
Graphics object consisting of 1 graphics primitive
>>> from sage.all import *
>>> __tmp__=var("x"); g = symbolic_expression(x**Integer(2)).function(x)
>>> g        # gはxをx^2に送る
x |--> x^2
>>> g(Integer(3))
9
>>> Dg = g.derivative(); Dg
x |--> 2*x
>>> Dg(Integer(3))
6
>>> type(g)
<class 'sage.symbolic.expression.Expression'>
>>> plot(g, Integer(0), Integer(2))
Graphics object consisting of 1 graphics primitive
g(x) = x^2
g        # gはxをx^2に送る
g(3)
Dg = g.derivative(); Dg
Dg(3)
type(g)
plot(g, 0, 2)

g は呼び出し可能シンボリック表現だが, g(x) の方はこれに関係はあっても異なる種類のオブジェクトである. やはりプロットと微積分などが可能なのだが,違っている点もあるので注意を要する. 以下の第5項で具体的に説明する.

sage: g(x)
x^2
sage: type(g(x))
<class 'sage.symbolic.expression.Expression'>
sage: g(x).derivative()
2*x
sage: plot(g(x), 0, 2)
Graphics object consisting of 1 graphics primitive
>>> from sage.all import *
>>> g(x)
x^2
>>> type(g(x))
<class 'sage.symbolic.expression.Expression'>
>>> g(x).derivative()
2*x
>>> plot(g(x), Integer(0), Integer(2))
Graphics object consisting of 1 graphics primitive
g(x)
type(g(x))
g(x).derivative()
plot(g(x), 0, 2)

3. Sageで定義済みの「初等関数」(calculus function)を使う. これらはプロット可能で,ちょっと工夫すると微分積分もできるようになる.

sage: type(sin)
<class 'sage.functions.trig.Function_sin'>
sage: plot(sin, 0, 2)
Graphics object consisting of 1 graphics primitive
sage: type(sin(x))
<class 'sage.symbolic.expression.Expression'>
sage: plot(sin(x), 0, 2)
Graphics object consisting of 1 graphics primitive
>>> from sage.all import *
>>> type(sin)
<class 'sage.functions.trig.Function_sin'>
>>> plot(sin, Integer(0), Integer(2))
Graphics object consisting of 1 graphics primitive
>>> type(sin(x))
<class 'sage.symbolic.expression.Expression'>
>>> plot(sin(x), Integer(0), Integer(2))
Graphics object consisting of 1 graphics primitive
type(sin)
plot(sin, 0, 2)
type(sin(x))
plot(sin(x), 0, 2)

そのままでは sin は微分演算を受けつけない. 少なくとも cos にはならない.

sage: f = sin
sage: f.derivative()
Traceback (most recent call last):
...
AttributeError: ...
>>> from sage.all import *
>>> f = sin
>>> f.derivative()
Traceback (most recent call last):
...
AttributeError: ...
f = sin
f.derivative()

sin そのままではなく f = sin(x) とすると微積分を受けつけるようになるが, もっと手堅いのは f(x) = sin(x) として呼び出し可能シンボリック表現を定義することである.

sage: S(x) = sin(x)
sage: S.derivative()
x |--> cos(x)
>>> from sage.all import *
>>> __tmp__=var("x"); S = symbolic_expression(sin(x)).function(x)
>>> S.derivative()
x |--> cos(x)
S(x) = sin(x)
S.derivative()

まだ注意を要する点が残っているので,説明しておこう:

  1. 意図しない評価が起きることがある.

sage: def h(x):
....:     if x < 2:
....:         return 0
....:     else:
....:         return x - 2
>>> from sage.all import *
>>> def h(x):
...     if x < Integer(2):
...         return Integer(0)
...     else:
...         return x - Integer(2)
def h(x):
    if x < 2:
        return 0
    else:
        return x - 2

ここで plot(h(x), 0, 4) を実行すると,プロットされるのは \(y=x-2\) で,複数行にわたって定義しておいた h ではない. 原因を考えてみよう. コマンド plot(h(x), 0, 4) が実行されると,まず h(x) が評価されるが, これは x が関数 h(x) に突っ込まれ x<2 が評価されることを意味する.

sage: type(x<2)
<class 'sage.symbolic.expression.Expression'>
>>> from sage.all import *
>>> type(x<Integer(2))
<class 'sage.symbolic.expression.Expression'>
type(x<2)

シンボリック式が評価される際, h の定義の場合と同じように,その式が明らかに真でないかぎり戻り値は偽になる. したがって h(x)x-2 と評価され,プロットされるのも x-2 になるわけである.

解決策はというと, plot(h(x), 0, 4) ではなく

sage: plot(h, 0, 4)
Graphics object consisting of 1 graphics primitive
>>> from sage.all import *
>>> plot(h, Integer(0), Integer(4))
Graphics object consisting of 1 graphics primitive
plot(h, 0, 4)

を実行せよ,ということになる.

5. 意図せず関数が定数になってしまう.

sage: f = x
sage: g = f.derivative()
sage: g
1
>>> from sage.all import *
>>> f = x
>>> g = f.derivative()
>>> g
1
f = x
g = f.derivative()
g

問題は,例えば g(3) などと実行するとエラーになって, "ValueError: the number of arguments must be less than or equal to 0."と文句をつけてくることだ.

sage: type(f)
<class 'sage.symbolic.expression.Expression'>
sage: type(g)
<class 'sage.symbolic.expression.Expression'>
>>> from sage.all import *
>>> type(f)
<class 'sage.symbolic.expression.Expression'>
>>> type(g)
<class 'sage.symbolic.expression.Expression'>
type(f)
type(g)

g は関数ではなく定数になっているので,変数を持たないから何も値を受けつけない.

解決策は何通りかある.

  • f を最初にシンボリック表式として定義しておく.

sage: f(x) = x        #  'f = x'とはしない
sage: g = f.derivative()
sage: g
x |--> 1
sage: g(3)
1
sage: type(g)
<class 'sage.symbolic.expression.Expression'>
>>> from sage.all import *
>>> __tmp__=var("x"); f = symbolic_expression(x        ).function(x)#  'f = x'とはしない
>>> g = f.derivative()
>>> g
x |--> 1
>>> g(Integer(3))
1
>>> type(g)
<class 'sage.symbolic.expression.Expression'>
f(x) = x        #  'f = x'とはしない
g = f.derivative()
g
g(3)
type(g)
  • または f の定義は元のまま g をシンボリック表式として定義する.

sage: f = x
sage: g(x) = f.derivative()  # 'g = f.derivative()'とするかわり
sage: g
x |--> 1
sage: g(3)
1
sage: type(g)
<class 'sage.symbolic.expression.Expression'>
>>> from sage.all import *
>>> f = x
>>> __tmp__=var("x"); g = symbolic_expression(f.derivative()  ).function(x)# 'g = f.derivative()'とするかわり
>>> g
x |--> 1
>>> g(Integer(3))
1
>>> type(g)
<class 'sage.symbolic.expression.Expression'>
f = x
g(x) = f.derivative()  # 'g = f.derivative()'とするかわり
g
g(3)
type(g)
  • または fg の定義は元のまま,代入すべき変数を特定する.

sage: f = x
sage: g = f.derivative()
sage: g
1
sage: g(x=3)    # たんに'g(3)'とはしない
1
>>> from sage.all import *
>>> f = x
>>> g = f.derivative()
>>> g
1
>>> g(x=Integer(3))    # たんに'g(3)'とはしない
1
f = x
g = f.derivative()
g
g(x=3)    # たんに'g(3)'とはしない

おしまいになったが, f = xf(x) = x 各々に対する微分の相違点を示す方法がまだあった.

sage: f(x) = x
sage: g = f.derivative()
sage: g.variables()  # gに属する変数は?
()
sage: g.arguments()  # gに値を送り込むための引数は?
(x,)
sage: f = x
sage: h = f.derivative()
sage: h.variables()
()
sage: h.arguments()
()
>>> from sage.all import *
>>> __tmp__=var("x"); f = symbolic_expression(x).function(x)
>>> g = f.derivative()
>>> g.variables()  # gに属する変数は?
()
>>> g.arguments()  # gに値を送り込むための引数は?
(x,)
>>> f = x
>>> h = f.derivative()
>>> h.variables()
()
>>> h.arguments()
()
f(x) = x
g = f.derivative()
g.variables()  # gに属する変数は?
g.arguments()  # gに値を送り込むための引数は?
f = x
h = f.derivative()
h.variables()
h.arguments()

ここの例から判るように, h(3) がエラーになるのは,そもそも h が引数を受けつけないためである.