LoginSignup
2
2

PowerShellコンソールを電卓代わりに使う人への贈りもの

Last updated at Posted at 2023-03-26

概要

PowerShell のコンソールはちょっとした計算をしたいときに電卓代わりに使えて便利です。
ただ、四則演算ならいいけど abs, max/min, round などの数学関数を使うにはそれらの関数名の前に [Math]::を付けないと駄目でそれがちょっと面倒くさい。特に、べき乗(累乗)の計算をする際に[Math]::Pow(x,y)を打つのが面倒で x^y または x**y で済ませたい。同じように感じている方に向けて PowerShell 電卓を使いやすくするためのツールを共有します。(Python 使ってるよって方には無用かな。)

使い方

下記のコードを拡張子「.psm1」のテキストファイルとして $env:PSModulePath に含まれるパスのいずれかの下に

psCalc
└ psCalc.psm1

のフォルダ構造で保存した後、PowerShellコンソールを起動してpscalcとタイプして呼び出すことができます1(インターネットにアクセス可能な環境であれば、もっと簡単に Install-Module psCalc を実行することでもインストール可能)。
使用例はコードのコメント文中にある「.EXAMPLE」を参照してください2
終了する場合はexitまたはquitのコマンドを実行するか、Ctrlキーを押下しながらCキーを押下します。

コード

独自に構文解析を行なうような本格的なプログラムではありません。入力された数式を PowerShell が理解できる形式に内部で書き換えて評価させる仕組みです(これはこれで正規表現が結構大変)。この方式だからこそ PowerShell の強力な機能を活かせるメリットがあると思ってます。

スクリプトを表示

psCalc.psm1

psCalc.psm1
using namespace System.Numerics
<#
    .SYNOPSIS
        A tool that enables the use of the PowerShell console as a scientific calculator.

    .DESCRIPTION
        Accepts standard mathematical expressions as input and outputs the results on the console by
        internally converting the expressions into forms that PowerShell can parse. 
        If no expression is specified as an argument, a read-eval-print-loop (REPL) session will start.
        Intended for interactive use.
    
    .PARAMETER Expr
        Specifies a mathematical expression to evaluate (optional). 
        It is recommended to use this in combination with the stop-parsing token (--%). 
    
    .INPUTS
        Expressions can be passed as the first argument or specified at REPL prompts. This function
        does not accept inputs from a pipeline.

    .OUTPUTS
        This function does not return a value. Outputs will be redirected to the host device.

    .NOTES
        Commands : exit, quit, verbose on, verbose off, ? (the last invoked expression), err (the last error)
        Constants: e (=2.71828...), pi (=3.14159...)
        img. unit: i (=[Comprex]::new(0,1))
        Functions: Abs, Acos, Acosh, Asin, Asinh, Atan, Atan2, Atanh, Ceiling, Cos, Cosh, Exp, Floor, 
                   Log, Log10, Log2, Max, Min, Pow, Round, Sign, Sin, Sinh, Sqrt, Tan, Tanh, Truncate
        UserFunc.: f1, f2, f3, g1, g2, g3, h1, h2, h3
        Operators: ! (factorial), ^/** (powers), % (remainder), *, +, -, /,
                   and other arithmetic operators that start with '-'
        Variables: $result (the result of the previous expression; 'ans' can be used instead.)
        Remarks  : - Two constants, 'pi' and 'e' (the base of the natural logarithms), are available.
                   - There is no need to prefix math functions such as 'Abs' with [Math]:: or [Bigint]::,
                     as [Math]:: or [Math_]:: are automatically added internally. Methods with the same 
                     name that call math functions contained in the System.Math, System.Numerics.BigInteger,
                     and System.Numerics.Complex classes are overloaded in the Math_ class.
                   - The power operator can be written as '^' or '**', and the factorial operator as '!'.
                   - PowerShell's arithmetic operators (starting with a minus sign, such as '-band') 
                     can be used.
                   - PowerShell functions can be defined and used. For example, you can define a function
                     as 'function f($x,$y){$x+$y}', and call it as 'f 3 4'. You can also write a function
                     definition as 'f($x,$y):=$x+$y'. If operations are performed on the result of a 
                     user-defined function, it must be enclosed in parentheses (e.g., '2 * pi * (f 3 4)').
                   - The following 9 functions can be used as usual, because they are rewritten as
                     class methods internally: f1, g1, h1 (functions with one argument), f2, g2, h2
                     (functions with two arguments), and f3, g3, h3 (functions with three arguments). 
                     For example, you can define a function as 'f2($x,$y):=$x^2+$y^2' and then call it
                     as 'f2(3,4)'.
                   - PowerShell variables (beginning with $) can be defined and used. Only alphanumeric
                     characters and underscores (_) can be used in user-defined variable names. Using other
                     characters in variable names may cause incorrect behavior.
                   - The result of the previous expression is stored in the $result variable and can be
                     referenced in the next expression. 'ans' can also be used to specify the last result.
                   - If you enter 'verbose on', subsequent sessions will output the internally converted
                     formulas. In addition, additional error messages are displayed when a syntax error
                     occurs. If you enter 'verbose off', the output will stop. Also, the last invoked
                     expression can be displayed using the '?' command.
                   - Suffixes for numeric literals such as 'd' (Decimal), 'l' (Long), and 'n' (BigInteger 
                     *in PowerShell 7.0 or later) can be specified (e.g. '0.1d'). If you want to 
                     explicitly specify the 'Double' type, you can use casting such as '[Double]0.1'.
                   - You can represent hexadecimal numbers in the format of '0xFFFFFFFF'. In addition, 
                     in PowerShell 7.0 or later, you can represent binary numbers in the format of
                     '0b11111111' and specify a suffix 'u' to indicate unsigned.
                   - Instead of creating an object like [Numerics.Complex]::new(1,-2), complex numbers
                     can also be written such as '1 - 2i'.
                   - If a real number is specified for an operation targeting integers, the number will
                     be rounded to the nearest integer before the operation.
                   - You can execute many of the PowerShell cmdlets.

        Author : earthdiver1
        Version: 1.11.1
        Licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.
        
    .EXAMPLE
        PS> pscalc --%  sqrt(2)
        => invoked expr.: [Math_]::sqrt(2)
        1.4142135623731

        PS> Invoke-MathExpression
        Calc> (1 + 2/3) * 4
        6.66666666666667
        Calc> sqrt( 5 )
        2.23606797749979
        Calc> 5^(2^-1)
        2.23606797749979
        Calc> $a = pi/4
        Calc> $a
        0.785398163397448
        Calc> sin( $a )
        0.707106781186547
        Calc> (sin( $a ))^2 + (cos( $a ))^2
        1
        Calc> tan( pi/4 )
        1
        Calc> atan( 1 )
        0.785398163397448
        Calc> ans * 4
        3.14159265358979
        Calc> 10!
        3628800
        Calc> ?
        => last invoked exp. : [Math_]::Fac(10)
        Calc> f1( $n ) := switch( $n ){ 0{1} 1{1} default{ $n * f1($n - 1) } }
        Calc> f1( 10 )
        3628800
        Calc> verbose on
        Calc> f2( $x, $y ) := $x * $x + $y * $y
        => invoked expression: function f2( $x, $y ) { $x * $x + $y * $y }
        Calc> sqrt( f2( 3 , 4 ) )
        => invoked expression: [Math_]::sqrt( [Math_]::f2( 3 , 4 ) )
        5
        Calc> verbose off
        Calc> f1( $x ) := f2( sin( $x ), cos( $x ) )
        Calc> f1( $a )
        1
        Calc> 2^2^2^2
        Syntax error
        Calc> ((2^2)^2)^2
        256
        Calc> 2^(2^(2^2))
        65536
        Calc> 2^256
        1.15792089237316E+77
        Calc> ([bigint]2)^256    # can be written as '2n^256' on PowerShell 7.0
        115792089237316195423570985008687907853269984665640564039457584007913129639936
        Calc> (1-2i)^3
        -11 + 2i
        Calc> exp(i)
        0.54030230586814 + 0.841470984807897i
        Calc> cos(1) + sin(1) * i
        0.54030230586814 + 0.841470984807897i
#>
function Invoke-MathExpression {
[Alias('psCalc')]
param( [Parameter(Position=0,ValueFromRemainingArguments)][string]$Expr )

class Math_ {
    static  [bigint] Abs(  [bigint]$x )             { return  [bigint]::Abs( $x )     }
    static  [object] Abs(  [object]$x )             { return    [Math]::Abs( $x )     }
    static  [double] Abs( [complex]$x )             { return [complex]::Abs( $x )     }
    static  [double] Acos(  [double]$x )            { return    [Math]::Acos( $x )    }
    static [complex] Acos( [complex]$x )            { return [complex]::Acos( $x )    }
    static  [double] Asin(  [double]$x )            { return    [Math]::Asin( $x )    }
    static [complex] Asin( [complex]$x )            { return [complex]::Asin( $x )    }
    static  [double] Atan(  [double]$x )            { return    [Math]::Atan( $x )    }
    static [complex] Atan( [complex]$x )            { return [complex]::Atan( $x )    }
    static  [double] Cos(  [double]$x )             { return    [Math]::Cos( $x )     }
    static [complex] Cos( [complex]$x )             { return [complex]::Cos( $x )     }
    static  [double] Cosh(  [double]$x )            { return    [Math]::Cosh( $x )    }
    static [complex] Cosh( [complex]$x )            { return [complex]::Cosh( $x )    }
    static  [double] Exp(  [double]$x )             { return    [Math]::Exp( $x )     }
    static [complex] Exp( [complex]$x )             { return [complex]::Exp( $x )     }
    static  [double] Log(  [bigint]$x )             { return  [bigint]::Log( $x )     }
    static  [double] Log(  [double]$x )             { return    [Math]::Log( $x )     }
    static [complex] Log( [complex]$x )             { return [complex]::Log( $x )     }
    static  [double] Log(  [bigint]$x, [double]$y ) { return  [bigint]::Log( $x, $y ) }
    static  [double] Log(  [double]$x, [double]$y ) { return    [Math]::Log( $x, $y ) }
    static [complex] Log( [complex]$x, [double]$y ) { return [complex]::Log( $x, $y ) }
    static  [double] Log10(  [bigint]$x )           { return  [bigint]::Log10( $x )   }
    static  [double] Log10(  [double]$x )           { return    [Math]::Log10( $x )   }
    static [complex] Log10( [complex]$x )           { return [complex]::Log10( $x )   }
    static  [bigint] Log2( [bigint]$x )             { return  [bigint]::Log2( $x )    }
    static  [double] Log2( [double]$x )             { return    [Math]::Log2( $x )    }
    static  [bigint] Max( [bigint]$x, [bigint]$y )  { return  [bigint]::Max( $x, $y ) }
    static  [object] Max( [object]$x, [object]$y )  { return    [Math]::Max( $x, $y ) }
    static  [bigint] Min( [bigint]$x, [bigint]$y )  { return  [bigint]::Min( $x, $y ) }
    static  [object] Min( [object]$x, [object]$y )  { return    [Math]::Min( $x, $y ) }
    static  [bigint] Pow(  [bigint]$x,    [int]$y ) { return  [bigint]::Pow( $x, $y ) }
    static  [double] Pow(  [double]$x, [double]$y ) { return    [Math]::Pow( $x, $y ) }
#   static [complex] Pow( [complex]$x, [double]$y ) { return [complex]::Pow( $x, $y ) }
    static [complex] Pow( [complex]$x, [double]$y ) {
        if ( $y -eq [Math]::Truncate($y) -and [Math]::Abs($y) -le 1023.0 ) {
            $n = [int]$y
            if ( $n -eq 0 ) { return [complex]::One }
            if ( $n -gt 0 ) {
                $p = [complex]::One
                while ( $n -gt 0 ) {
                    if ( $n -band 1 ) { $p *= $x }
                    $x *= $x
                    $n = $n -shr 1
                }
                return $p
            } else {
                return 1.0 / [Math_]::Pow( $x, -$y )
            }
        }
        return [complex]::Pow( $x, $y )
    }
    static  [double] Sin(  [double]$x )             { return    [Math]::Sin( $x )     }
    static [complex] Sin( [complex]$x )             { return [complex]::Sin( $x )     }
    static  [double] Sinh(  [double]$x )            { return    [Math]::Sinh( $x )    }
    static [complex] Sinh( [complex]$x )            { return [complex]::Sinh( $x )    }
#   static  [double] Sqrt(  [double]$x )            { return    [Math]::Sqrt( $x )    }
    static  [object] Sqrt(  [double]$x )            {
        if ( $x -lt 0 ) { return [complex]::new( 0, [Math]::Sqrt( -$x ) ) }
        return [Math]::Sqrt( $x )
    }
    static [complex] Sqrt( [complex]$x )            { return [complex]::Sqrt( $x )    }
    static  [double] Tan(  [double]$x )             { return    [Math]::Tan( $x )     }
    static [complex] Tan( [complex]$x )             { return [complex]::Tan( $x )     }
    static  [double] Tanh(  [double]$x )            { return    [Math]::Tanh( $x )    }
    static [complex] Tanh( [complex]$x )            { return [complex]::Tanh( $x )    }
    static  [bigint] Fac(  [bigint]$n ) {
        if ( $n -lt [bigint]::Zero ) { throw "Negative number specified for factorial." }
        $maxdigits = 100000.
        $x = [double]$n
        if ( [Math]::Abs( 0.5 * [Math]::Log10(  2 * [Math]::PI * $x ) + 
                           $x * [Math]::Log10( $x / [Math]::E       )   ) -gt $maxdigits ) {
            throw "Number of digits exceeded the upper limit(=$maxdigits)."
        }
        $f = [bigint]::One
        while ( $n -gt [bigint]::One ) {
            $f *= $n
            $n -= [bigint]::One
        }
        return $f
    }
    static [object] Fac( [object]$x ) {
        $n = [long]$x
        if ( $n -lt 0 ) { throw "Negative number specified for factorial." }
        if ( $n -gt 170 ) {
            return [double]::PositiveInfinity
        } elseif ( $n -gt 27 ) {
            return 1..$n | & { begin{ $f = 1.0 } process{ $f *=  [double]$_ } end{ $f } }
        } elseif ( $n -gt 1 ) {
            return 1..$n | & { begin{ $f = 1.d } process{ $f *= [decimal]$_ } end{ $f } }
        } else {
            return 1d
        }
    }
    static [object] f1( [object]$x )                         { return (f1 $x)       }
    static [object] g1( [object]$x )                         { return (g1 $x)       }
    static [object] h1( [object]$x )                         { return (h1 $x)       }
    static [object] f2( [object]$x, [object]$y )             { return (f2 $x $y)    }
    static [object] g2( [object]$x, [object]$y )             { return (g2 $x $y)    }
    static [object] h2( [object]$x, [object]$y )             { return (h2 $x $y)    }
    static [object] f3( [object]$x, [object]$y, [object]$z ) { return (f3 $x $y $z) }
    static [object] g3( [object]$x, [object]$y, [object]$z ) { return (g3 $x $y $z) }
    static [object] h3( [object]$x, [object]$y, [object]$z ) { return (h3 $x $y $z) }
}

$params = @{
    TypeName   = 'System.Numerics.Complex'
    MemberType = 'ScriptMethod'
    MemberName = 'ToString2'
    Value      = {
        param( [string]$format = "" )
        if ( $this.Real -ne 0.0 ) {
            $cmplx = "$($this.Real.ToString($format))"
            if ( $this.Imaginary -gt 0.0 ) { $cmplx += " + $( $this.Imaginary.ToString($format))i" }
            if ( $this.Imaginary -lt 0.0 ) { $cmplx += " - $(-$this.Imaginary.ToString($format))i" }
        } else {
            $cmplx = "0"
            if ( $this.Imaginary -ne 0.0 ) { $cmplx = "$($this.Imaginary.ToString($format))i"      }
        }
        return $cmplx -replace '(?<![.0-9])1i$','i'
    }
}
Update-TypeData @params -Force

$options = [Text.RegularExpressions.RegexOptions]'ECMAScript, IgnoreCase'
$iunit   = [complex]::ImaginaryOne
$e       = [Math]::E
$pi      = [Math]::PI
$binhex  = '\b(?:0b[01]+|0x[0-9a-f]+)[inu]?\b'
$integer = '(?<![\w.])[0-9]+[iln]?(?![\w.])'
$decimal = '(?<![\w.])(?:[0-9]+(?:\.[0-9]*|(?=[de]))|\.[0-9]+)(?:e[+-]?[0-9]+)?[din]?(?![\w.])'
$number  = "(?:$binhex|$integer|$decimal)"
$usrvar  = '\$[0-9_a-z]+(?:\.[0-9_a-z]+|\[[^\]]+\])?(?![\w.[])'
#$strwip  = '\((?>(?:[^()]+|(?<o>\()|(?<-o>\)))*)(?(o)(?!))\)'                      # string within parenthesis (similar to '(?<p>\((?>(?:[^()]+|(?&p))*)\))' or '(?<p>\((?>[^()]*(?:(?&p)[^()]*)*)\))' with PCRE)
$strwip  = '\((?>[^()]*(?:(?:(?<o>\()[^()]*)+(?:(?<-o>\))[^()]*)+)*)(?(o)(?!))\)'  # string within parenthesis
$operand = "(?:$number|$usrvar|\B$strwip)"
$mathfn1 = "(?<!]::)\b((?:Acosh|Asinh|Atan2|Atanh|Ceiling|Floor|Round|Sign|Truncate)$strwip)"
$mathfn2 = "(?<!]::)\b((?:Abs|Acos|Asin|Atan|Cos|Cosh|Exp|Log|Log10|Log2|Max|Min|Pow|Sin|Sinh|Sqrt|Tan|Tanh)$strwip)"
$factor  = "(?<!(?:\*\*|\^)\s*)($operand)\s*!(?!\s*(?:\*\*|[!^]))"
$powers  = "(?<!(?:\*\*|\^)\s*)($operand)\s*(?:\*\*|\^)\s*([+-]?\s*$operand)(?!\s*(?:\*\*|[!^]))"
$usrfdef = '^\s*([a-z][0-9_a-z]*\s*(?:\([^)]*\))?)\s*:=\s*(.+)\s*$'
$usrfn   = "(?<!(?:\bfunction\s+|]::))\b([fgh][123]$strwip)"
$vars    = @('$e','$pi','$binhex','$cmplx','$decimal','$expression','$factor','$integer','$iunit',
             '$lastex','$mathfn1','$mathfn2','$number','$operand','$options','$powers','$strwip',
             '$usrfdef','$usrfn','$usrvar','$verbose','$var','$vars')

$eval = {
    $expression = [regex]::Replace($expression, '\bans\b', '$$result', $options)
    $expression = [regex]::Replace($expression, '\bi\b', '$$iunit', $options)
    $expression = [regex]::Replace($expression, "($number)(?<=i)", "(`$1`0*`$`$iunit)", $options).Replace("i`0",'')
    $expression = [regex]::Replace($expression, '(?<!]::)\bPi\b', '$$pi',$options)
    $expression = [regex]::Replace($expression, '(?<!]::)\bE\b', '$$e' ,$options)
    $expression = [regex]::Replace($expression, $usrfdef, 'function $1 { $2 }', $options)
    while ( [regex]::IsMatch($expression, $mathfn1, $options) ) {
        $expression = [regex]::Replace($expression, $mathfn1, '[Math]::$1', $options)
    }
    while ( [regex]::IsMatch($expression, $mathfn2, $options) ) {
        $expression = [regex]::Replace($expression, $mathfn2, '[Math_]::$1', $options)
    }
    while ( [regex]::IsMatch($expression, $usrfn, $options) ) {
        $expression = [regex]::Replace($expression, $usrfn, '[Math_]::$1', $options)
    }
    while ( [regex]::IsMatch($expression, $factor, $options) ) {
        $expression = [regex]::Replace($expression, $factor, '[Math_]::Fac($1)', $options)
    }
    while ( [regex]::IsMatch($expression, $powers, $options) ) {
        $expression = [regex]::Replace($expression, $powers, '[Math_]::Pow($1,$2)', $options)
    }
    $expression = $expression.Replace('[Math_]::Pow($e,', '[Math_]::Exp(')
    if ( $verbose ) { Write-Host "=> invoked expr.: $expression" }
    try {
        $lastex = $expression
        ( $result = Invoke-Expression $expression ) | ForEach-Object {
            if ( $_ -and $_.GetType().Name -eq "Complex" ) { $_.ToString2() } else { $_ }
        } | Out-Host
    } catch {
        if ( $_.Exception.WasThrownFromThrowStatement ) {
            Write-Host $_.Exception.Message -ForegroundColor Red
        } else {
            Write-Host "Syntax error" -ForegroundColor Red
            if ( $verbose ) {
                Write-Host $_.Exception.Message -BackgroundColor Black -ForegroundColor Yellow
            }
        }
    }
}

$expression = $Expr.Replace("--% ","") -replace '^-E(x(pr?)?)? '
$verbose = $expression -ne ""

if ( $expression ) {
    . $eval
} else {
    $lastex = ""
    $result = ""
:loop 
    while ( $true ) {
        Write-Host "Calc> " -NoNewLine
        $expression = ( $Host.UI.ReadLine() -replace '#.*$' ).Trim()
        foreach ( $var in $vars ) {
            if ( $expression -match "\$var\b" ) {
                Write-Host "Invalid variable name. '$var' is reserved for the program." -ForegroundColor Red
                continue loop
            }
        }
        switch -Regex ( $expression ) {
            '^(exit|quit)$'   { break loop }
            '^$'              { continue loop }
            '^verbose\s+on$'  { $verbose = $true ; continue loop }
            '^verbose\s+off$' { $verbose = $false; continue loop }
            '^\?$'            { Write-Host "=> prev. expr.  : $lastex" }
            '^err$'           { Write-Host "=> last error   : $($global:Error[0].Exception.Message)" }
            default           { . $eval }
        }
    }
}
} # end of function block
Export-ModuleMember -Function Invoke-MathExpression -Alias psCalc

備考

pi(円周率)と e(自然対数の底)の2つの定数を使用できます。
Abs などの数学関数に対して [Math]::[Bigint]:: などを付ける必要がありません3
・べき乗(累乗)の演算子として ^ または ** を使用できます4
・階乗のオペレータとして ! を使用できます。
・PowerShellの(-band などのマイナス記号で始まる)算術演算子を使用できます。
・PowerShellの関数を定義して利用することが可能です(function f($x,$y){$x+$y} など)5。関数定義を f($x,$y):=$x+$y のように書くこともできます。PowerShellの関数は呼び出し方法はやや特殊(f 3 4 など)なので気を付けてください。引数が1つの場合は f(3) のように指定することも可能ですが、ユーザ定義関数の結果に対して演算を行なう場合には括弧で囲む必要があります。(例:2 * pi * (f(3)) )
・引数を1つとる関数 f1,g1,h1、2つとる関数 f2,g2,h2 そして3つとる関数 f3,g3,h3 の計9個の関数については、内部でクラスメソッドへの書き換えを行うため通常の感覚で使用できます。例えば関数を f2($x,$y):=$x^2+$y^2 と定義した後 f2(3,4) で呼び出します。
・PowerShellの変数($で始まる)を定義して利用することが可能です。変数名には、英数字とアンダースコア(_)のみ使用できます。それ以外の文字を変数名に使用した場合には動作がおかしくなる可能性があります。
・1つ前に評価した数式の結果が $result に格納されており、次の数式中で参照することが出来ます。また、ans も最後の結果を指定するために使用できます。
・verbose on を入力すると、それ以降のセッションでは内部的に変換された数式が出力されます。また、Syntax Error 発生時に追加のエラーメッセージを表示します。verbose off で出力を停止します。なお、最後に呼び出された式を ? コマンドで表示することもできます。
・数値リテラルに d(Decimal), l(Long), n(BigInteger ※PowerShell 7.0以降)のサフィックスを指定できます(例:0.1d)。明示的にDouble型を指定したい場合には例えば [Double]0.1 のようにキャストしてください。
・16進数を 0xFFFFFFFF のような形式で表現することができます。また、PowerShell 7.0以降では2進数を 0b11111111 のような形式で表すことや、符号なし(Unsigned)を示すサフィックスuを指定することができます。
・複素数は、[Complex]::new(1,-2) のようにオブジェクトを作成6する代わりに 1 - 2i のように書くことができます。
・整数を対象とする演算に対して実数が指定された場合は、演算に先立ち小数の丸め処理が行なわれます。
・powershellの多くのコマンドレットも実行可能です。

関数の評価について

数学関数を使用する場合の内部処理について解説します。

Calc> sin( pi/2 )

と入力した場合、スクリプト内部で [Math_]::sin( $pi/2 ) と変換されます。
$pi はスクリプト中で $pi = [Math]::PI と定義されており、$pi/2 は円周率の半分の 1.5707... と評価されることになります。この数値を引数に指定された Math_ クラスの Sin メソッドは、下記のようにDouble型の引数を持つものと Complex型を引数に持つものがオーバーロードされていますが、この入力(Double型の 1.57...)の場合は Double型の引数を持つ方のメソッドが実行されます。Double型の引数を持つ方の Sin メソッドは、引数に指定された値をそのまま Math クラスの Sin メソッドに渡して実行します。

class Math_ {
    (中略)
    static  [double] Sin(  [double]$x ) { return    [Math]::Sin( $x ) }    ⬅ こちらが実行される
    static [complex] Sin( [complex]$x ) { return [complex]::Sin( $x ) }
    (中略)
}

結果的に、上記の入力式の場合は [Math]::Sin( [Math]::PI / 2 ) を評価した場合と同じ結果が得られます。

次にユーザ定義関数を使用する場合の内部処理について解説します。

Calc> g2 ( $x , $y ) :=  $x * $x + $y * $y

と入力すると、内部で function g2 ( $x , $y ){ $x * $x + $y * $y } と変換・実行されて関数 g2 が定義されます。
次に

Calc> g2 3 4

と入力した場合、関数 g2 がそのまま実行されて結果 25 を得ます。一方、

Calc> g2(3,4)

と入力した場合は、内部的に [Math_]::g2(3,4) と書き換えられて Math_ クラス内で定義されている g2 メソッドが実行されます。
Math_ クラス内で g2 メソッドは下記のように定義されていて、結局、関数 g2(g2 3 4) のように呼び出されて同じ結果 25 を得ることになります。

class Math_ {
    (中略)
    static [object] g2( [object]$x, [object]$y ) { return (g2 $x $y) }
    (中略)
}

結局、同じ結果になる訳ですが、クラスメソッドの方が通常の数学表記に近い書き方ができるメリットがあるのでこのような仕組みを採用しています。

他のお薦めコマンドライン型電卓

  1. PowerShell 7系ではエイリアスに対する暗黙的なモジュールのインポートを有効にするために別途モジュールマニフェストが必要です。@{ RootModule = 'psCalc.psm1'; ModuleVersion = '1.11.0'; AliasesToExport = 'psCalc' } の内容のテキストファイルを psCalc.psd1 のファイル名で同じフォルダ内に格納してください。「実行ポリシー」の変更を求められる場合があります。実行ポリシーについての説明はこちらの記事をご参照ください。

  2. モジュールが正しく設置できている場合は、Get-Help psCalc -Examples でも参照することができます。

  3. 内部で自動的に [Math]:: または [Math_]:: を補います。System.Math,System.Numerics.BigInteger, System.Numerics.Complexの各クラスに含まれる数学関数を呼び出す同名のメソッドが Math_クラスの中でオーバーロードされています。

  4. Complex型を数値に対するべき乗には [Numerics.Complex]::Pow() を用いていますが、結果に現れる端数が気になることがあったため、指数が -1023 から 1023 の整数の場合は乗算によって計算するよう変更しました。(例えば i^2[Numerics.Complex]::Pow([Numerics.Complex]::ImaginaryOne,2) で計算すると (-1,0) を期待しているところが PowerShell 5.1では (-1,1.22460635382238E-16) となる。)

  5. 定義は1行に収まる必要があります。

  6. using namespace System.Numerics を内部で実行しているため、[Numerics.Complex] の代わりに [Complex] を使用することができます。

2
2
3

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