• ホーム
  • C#
  • C#で数式を中置記法から後置記法(逆ポーランド記法)に変換してみた(三角関数編)

C#で数式を中置記法から後置記法(逆ポーランド記法)に変換してみた(三角関数編)

以前投稿した「C#で数式を中置記法から後置記法(逆ポーランド記法)に変換してみた」では、四則演算と括弧だけの数式を対象にしていました。実際には三角関数を利用する機会がありますので、三角関数に対応させてみます。

目次

  1. 逆ポーランド記法における三角関数
  2. スタックで逆ポーランド記法に変換するときに三角関数をどう扱うか
  3. 試してみた
    1. ビュー
    2. コード
    3. 動かしてみた

逆ポーランド記法における三角関数

ウィキペディアでは三角関数には触れていませんが、そこに記載されている定義では

逆ポーランド記法(ぎゃくポーランドきほう、英語: Reverse Polish Notation, RPN)は、数式やプログラムの記法の一種。演算子を被演算子の後にすることから、後置記法 (Postfix Notation) とも言う。

となっていますので、数値の後ろに関数名を置いてみます。

スタックで逆ポーランド記法に変換するときに三角関数をどう扱うか

「単項演算子の場合はどうするの?」「加減乗除と三角関数は優先順位どうなるの?」「場合分けするのか?」などといろいろ考えてしまいました。 ふと思ったのですが、三角関数には常に括弧が付いてきます。(括弧を付けるように運用を制限します。) つまり、逆ポーランド記法に変換する過程で、括弧の対を消滅させたときに演算子を積んでいる方のスタックの最上段に積まれているものが、三角関数か否かを判断すれば良いのではないでしょうか。

試してみた

というわけで、さっそく試してみました。

ビュー

C#で数式を中置記法から後置記法(逆ポーランド記法)に変換してみたと同じです。

<Window x:Class="trial_rpn.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:trial_rpn"
        mc:Ignorable="d"
        Title="MainWindow" Height="200" Width="300">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="40" />
            <RowDefinition Height="40" />
            <RowDefinition Height="1*" />
        </Grid.RowDefinitions>
        <TextBox Name="textbox" Grid.Row="0" Margin="5" />
        <Button Name="button" Content="変換" Grid.Row="1" Margin="5" Click="button_Click" />
        <TextBlock Name="textblock" Grid.Row="2" Margin="5" />
    </Grid>
</Window>

コード

  • サインをs、コサインをc、タンジェントをtで表すものとします。

  • 三角関数の引数は、必ず括弧でくくるものとします。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;

namespace trial_rpn
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void button_Click(object sender, RoutedEventArgs e)
        {
            Stack<char> buffer = new Stack<char>(); // バッファ
            Stack<char> st = new Stack<char>(); // 作業用のスタック
            textblock.Text = string.Empty;

            foreach(char token in textbox.Text)
            {
                switch (token)
                {
                    case '(':
                    case 'c':
                    case 's':
                    case 't':
                        st.Push(token);
                        break;
                    case ')':
                        while (st.Count>0)
                        {
                            char t = st.Pop();
                            if (t == '(')
                            {
                                break;
                            }else
                            {
                                buffer.Push(t);
                            }
                        }
                        if (st.Count > 0)
                        {
                            if (st.Peek() == 'c' || st.Peek() == 's'|| st.Peek() == 't' )
                            {
                                buffer.Push(st.Pop());
                            }
                        }
                        break;
                    case '*':
                    case '/':
                        while (st.Count > 0)
                        {
                            if (st.Peek() == '*' || st.Peek() == '/')
                            {
                                buffer.Push(st.Pop());
                            }else
                            {
                                break;
                            }
                        }
                        st.Push(token);
                        break;
                    case '+':
                    case '-':
                        while (st.Count > 0)
                        {
                            if (st.Peek() == '*' || st.Peek() == '/' || st.Peek()=='+'||st.Peek()=='-')
                            {
                                buffer.Push(st.Pop());
                            }else
                            {
                                break;
                            }
                        }
                        st.Push(token);
                        break;
                    default: // 数字の場合
                        buffer.Push(token);
                        break;
                }
            }
            while (st.Count > 0) // スタックが空になるまでpopしてバッファへ移動
            {
                buffer.Push(st.Pop());
            }
            string r = new String(buffer.Reverse().ToArray<char>()); // スタックの順番を逆順にして文字列に変換
            textblock.Text = textblock.Text + r + "\r\n";
        }
    }
}

switch文の条件に、tokenが三角関数のときは演算子用のスタックにそのまま積むように指定しました。 また、tokenが閉じ括弧の時の処理の最後(閉じ括弧をpopした後)に、演算子用のスタックの最上段を参照して三角関数だった場合はそれを数式用のスタックに積み替えるように指定しました。

動かしてみた

動かしてみました。 image0

1+2*s(3+c(4*t(5)+6))/7 → 12345t*6+c+s*7/+

え~と、逆ポーランド記法を順番に計算しますよ。

    5t           → tan(5)
   45t*          → 4*tan(5)
   45t*6+        → 4*tan(5)+6
   45t*6+c       → cos(4*tan(5)+6)
  345t*6+c+      → 3+cos(4*tan(5)+6)
  345t*6+c+s     → sin(3+cos(4*tan(5)+6))
 2345t*6+c+s*    → 2*sin(3+cos(4*tan(5)+6))
 2345t*6+c+s*7/  → 2*sin(3+cos(4*tan(5)+6))/7
12345t*6+c+s*7/+ → 1+2*sin(3+cos(4*tan(5)+6))/7

合ってるっぽいですね。

公開日

広告