Pythonにsign
関数がない理由を理解できません。 abs
ビルトイン(私はsign
の姉妹と考えています)がありますが、sign
はありません。
python 2.6にはcopysign
関数さえあります( math にあります)が、符号はありません。単にsign
を書いてからcopysign(x,y)
からcopysign
を直接取得できるのに、なぜabs(x) * sign(y)
を書くのが面倒なのですか?後者はより明確になります:xの符号はyですが、copysignの場合、xがyの符号かxの符号がyかを覚えておく必要があります!
明らかにsign(x)
はcmp(x,0)
以上のものを提供しませんが、これよりもはるかに読みやすくなります(そしてPythonのような非常に読みやすい言語の場合、これは大きなプラスになります)。
もし私がpythonデザイナーだったら、私は反対のやり方をしていたでしょう:cmp
ビルトインではなく、sign
です。 cmp(x,y)
が必要な場合は、sign(x-y)
を実行できます(または、数値以外のものの場合はax> yになります-もちろん、代わりにブール値を受け入れる必要がありますsorted
整数コンパレータの)。これはより明確になります:x>y
の場合は正(一方、cmp
の場合、firstの場合は正の規則を覚えておく必要がありますはbiggerですが、逆の場合もあります)。もちろん、cmp
は、他の理由(たとえば、非数値のものをソートする場合、または単純なブール値では使用できないソートを安定させる場合)のために、それ自体で意味があります。
それで、質問は:なぜPythonデザイナーがsign
関数を言語から除外することにしたのですか?なぜ親はcopysign
ではなくsign
に悩まされるのですか?
何か不足していますか?
編集-ピーター・ハンセンのコメントの後。あなたはそれを使用しなかったが、あなたはpythonを何のために使用したかについては言わなかった。 pythonを使用して7年で、何度も必要になりました。最後は、ラクダの背中を壊したストローです!
はい、cmpを渡すことができますが、私がそれを渡すのに必要な時間の90%は、lambda x,y: cmp(score(x),score(y))
のようなイディオムで、これは符号でうまく機能します。
最後に、sign
がcopysign
よりも便利であることに同意してくれることを願っています。それで、私があなたの見解を買ったとしても、なぜ符号ではなく数学でそれを定義することに悩むのですか? copysignはどうすれば署名よりも便利なのでしょうか?
編集:
実際、 math にsign()
を含む patch がありましたが、同意しなかったため、受け入れられませんでした すべてのEdgeケースで返すべきもの (+/- 0、+ /-nanなど)
そこで、彼らはコピーサインのみを実装することにしました。これは(より詳細ですが)エッジケースの目的の動作をエンドユーザーに委任するために 使用できます -( cmp(x,0)
を呼び出します。
なぜビルトインではないのかわかりませんが、いくつかの考えがあります。
copysign(x,y):
Return x with the sign of y.
最も重要なことは、copysign
はsign
のスーパーセットです! x = 1でcopysign
を呼び出すことは、sign
関数と同じです。したがって、単にcopysign
を使用し、それを忘れるだけです。
>>> math.copysign(1, -4)
-1.0
>>> math.copysign(1, 3)
1.0
2つの引数全体を渡すことにうんざりしている場合は、この方法でsign
を実装できますが、それでも他の人が言及しているIEEEのものと互換性があります。
>>> sign = functools.partial(math.copysign, 1) # either of these
>>> sign = lambda x: math.copysign(1, x) # two will work
>>> sign(-4)
-1.0
>>> sign(3)
1.0
>>> sign(0)
1.0
>>> sign(-0.0)
-1.0
>>> sign(float('nan'))
-1.0
第二に、通常、何かのサインが必要な場合、それを別の値で乗算するだけです。そしてもちろん、それは基本的にcopysign
が行うことです。
したがって、代わりに:
s = sign(a)
b = b * s
あなたはただすることができます:
b = copysign(b, a)
そして、はい、あなたがPythonを7年間使用していて、cmp
が非常に簡単に削除され、sign
に置き換えられると思います! __cmp__
メソッドを持つクラスを実装したことはありませんか? cmp
を呼び出してカスタムコンパレータ関数を指定したことはありませんか?
要約すると、私もsign
関数が必要であることがわかりましたが、最初の引数が1であるcopysign
はうまく機能します。 sign
はcopysign
よりも便利であることには同意しません。これは、同じ機能の単なるサブセットであることを示したためです。
「copysign」はIEEE 754で定義されており、C99仕様の一部です。それがPythonにある理由です。この関数は、NaN値の処理方法が原因で、abs(x)* sign(y)で完全に実装することはできません。
>>> import math
>>> math.copysign(1, float("nan"))
1.0
>>> math.copysign(1, float("-nan"))
-1.0
>>> math.copysign(float("nan"), 1)
nan
>>> math.copysign(float("nan"), -1)
nan
>>> float("nan") * -1
nan
>>> float("nan") * 1
nan
>>>
これにより、copysign()はsign()よりも便利な関数になります。
IEEEのsignbit(x)が標準Pythonで利用できない特定の理由については、わかりません。推測することはできますが、推測になります。
Mathモジュール自体は、xが負か非負かをチェックする方法としてsignbit(1、x)を使用します。数学関数を扱うほとんどの場合、1、0、または-1を返すsign(x)を使用するよりも便利だと思われます。これは、考慮すべきケースが1つ少ないためです。たとえば、次はPythonの数学モジュールのものです。
static double
m_atan2(double y, double x)
{
if (Py_IS_NAN(x) || Py_IS_NAN(y))
return Py_NAN;
if (Py_IS_INFINITY(y)) {
if (Py_IS_INFINITY(x)) {
if (copysign(1., x) == 1.)
/* atan2(+-inf, +inf) == +-pi/4 */
return copysign(0.25*Py_MATH_PI, y);
else
/* atan2(+-inf, -inf) == +-pi*3/4 */
return copysign(0.75*Py_MATH_PI, y);
}
/* atan2(+-inf, x) == +-pi/2 for finite x */
return copysign(0.5*Py_MATH_PI, y);
ここで、copysign()は3値のsign()関数よりも効果的な関数であることが明確にわかります。
あなたが書いた:
もし私がpythonデザイナーなら、私は逆のやり方をしていました:cmp()ビルトインではなく、sign()
つまり、数値以外のものにcmp()が使用されることを知らないということです。 cmp( "This"、 "That")は、sign()関数では実装できません。
追加の回答を他の場所で照合するために編集:
Abs()とsign()が頻繁に一緒に表示される方法に基づいて正当化を行います。 C標準ライブラリには「sign(x)」関数が含まれていないため、ビューを正当化する方法がわかりません。 abs(int)とfabs(double)とfabsf(float)とfabsl(long)がありますが、符号については言及されていません。 「copysign()」と「signbit()」がありますが、これらはIEEE 754番号にのみ適用されます。
複素数の場合、Pythonでsign(-3 + 4j)は何を返しますか? abs(-3 + 4j)は5.0を返します。これは、sign()が意味をなさない場所でabs()を使用する方法の明確な例です。
Pythonにabs(x)を補完するものとしてsign(x)が追加されたとします。 「x」が__abs __(self)メソッドを実装するユーザー定義クラスのインスタンスである場合、abs(x)はx .__ abs __()を呼び出します。正しく動作するために、abs(x)を同じ方法で処理するには、Pythonはsign(x)スロットを獲得する必要があります。
これは、比較的不要な機能には過剰です。それに、なぜsign(x)は存在し、nonnegative(x)とnonpositive(x)は存在しないのでしょうか? Pythonの数学モジュール実装のスニペットは、copybit(x、y)を使用してnonnegative()を実装する方法を示していますが、これは単純なsign(x)ではできません。
Pythonは、IEEE 754/C99数学関数をより適切にサポートする必要があります。それはsignbit(x)関数を追加し、floatの場合に望むことをします。整数や複素数では機能せず、文字列ははるかに少なく、探している名前はありません。
「なぜ」と尋ねると、答えは「sign(x)は役に立たない」です。あなたはそれが有用であると断言します。しかし、あなたのコメントは、あなたがその主張をするのに十分な知識がないことを示しています。それは、あなたがその必要性の説得力のある証拠を示さなければならないことを意味します。 NumPyがそれを実装していると言っても、十分な説得力はありません。符号関数を使用して既存のコードを改善する方法の事例を示す必要があります。
また、StackOverflowの範囲外です。代わりにPythonリストの1つに持って行ってください。
Sign()のもう1つのライナー
sign = lambda x: (1, -1)[x<0]
X = 0に対して0を返したい場合:
sign = lambda x: x and (1, -1)[x<0]
cmp
は 削除 であるため、同じ機能を次のように使用できます。
def cmp(a, b):
return (a > b) - (a < b)
def sign(a):
return (a > 0) - (a < 0)
float
、int
、さらにはFraction
に対しても機能します。 float
の場合、sign(float("nan"))
がゼロであることに注意してください。
Pythonは、比較がブール値を返すことを必要としないため、bool()に比較を強制することで、許可されているが一般的でない実装から保護されます。
def sign(a):
return bool(a > 0) - bool(a < 0)
Wikipediaの定義 読み取り:
したがって、
sign = lambda x: -1 if x < 0 else (1 if x > 0 else 0)
この関数定義 fast を実行し、を保証 の正しい結果、0.0、-0.0、-4、5(他の誤った回答へのコメントを参照)。
numpyにはサイン機能があり、他の機能のボーナスも提供します。そう:
import numpy as np
x = np.sign(y)
結果がnumpy.float64であることに注意してください。
>>> type(np.sign(1.0))
<type 'numpy.float64'>
Jsonはnumpy.float64型をシリアル化する方法を知らないので、jsonのように重要です。その場合、次のことができます。
float(np.sign(y))
通常のフロートを取得します。
はい、正しい sign()
関数は少なくともmathモジュールにあるべきです-numpyにあるように。数学指向のコードには頻繁に必要になるからです。
しかし、math.copysign()
も独立して役立ちます。
cmp()
およびobj.__cmp__()
...は、一般に独立して重要度が高くなっています。数学指向のコードだけではありません。タプル、日付オブジェクトなどを比較/ソートすることを検討してください。
math.sign()
の省略に関する http://bugs.python.org/issue164 のdev引数は奇妙です。
-NaN
はありませんsign(nan) == nan
(exp(nan)
のように)sign(-0.0) == sign(0.0) == 0
心配なしsign(-inf) == -1
心配なし-numpyのように
Python 2では、cmp()
は整数を返します。結果が-1、0、または1である必要はないため、sign(x)
はcmp(x,0)
と同じではありません。 。
Python 3では、cmp()
が削除され、豊富な比較が採用されました。 cmp()
、Python 3の場合 これを推奨 :
def cmp(a, b):
return (a > b) - (a < b)
これはcmp()には問題ありませんが、比較演算子は booleans を返す必要がないため、再びsign()に使用することはできません。
この可能性に対処するには、比較結果をブール値に強制する必要があります。
def sign(a):
return bool(x > 0) - bool(x < 0)
これは、完全に順序付けられたtype
(NaN
や無限などの特別な値を含む)に対して機能します。
あなたはそれを必要としません、あなたはただ使うことができます:
If not number == 0:
sig = number/abs(number)
else:
sig = 0