matlabで無名関数に代入の必要な複数行を実行させる方法

参考:http://woodrush.hatenablog.com/entry/2014/08/25/034413
逆参考: https://jp.mathworks.com/matlabcentral/answers/499533-matlab-r2019a-substr?s_tid=mwa_osa_a
(「逆参考」のページに、ほとんど同じ内容を投稿しています。)


ここでは、`substr`関数を実装することを考えていきます。

# 例

`substr = (@(s_or_c, i, n)feval(@(c, i, n) c(i:(i+n-1)),feval(@(s_or_c) char(s_or_c),s_or_c),i,n));`
これをコマンドラインにコピペすれば、substrを書き換えたりclear substrしたりしない限り、substrが利用可能になります。
`substr("hello world", 2, 6)`
と打てば「`'ello w'`」が返ります。

#解説

入力`c`が文字ベクトル(1重引用符で括られたもの)のとき、
「無名関数」というのを利用して定義すればいいです。
`substr = @(c,i,n) c(i:(i+n-1));`
とすることで、`substr`が実現します。

無名関数
`@(x, ..) 関数や演算子など(x, ..);`
は、

```matlab:anonymous.m
function rtn = anonymous(x, ..)
    rtn = 関数や演算子など(x, ..)
end
```
のような`anonymous.m`が「あるとみなして」`anonymous(x, ...)`を返してくれという命令です。

つまり、先ほどのコードでは、

```matlab
function rtn = anonymous(c, i, n)
    rtn = c(i:(i+n-1));
end
```
がある
とみなして、
`substr = anonymous(c,i,n);`
をやったのと同じです。

例えば`substr('hello world', 2, 6)`は`'ello w'`を返却します。

しかし、ここにはまだ問題が残っています。
`substr("hello world", 2, 6)`
は**機能しない**のです。
**2重引用符で括られたものは「文字列」であり、文字ベクトルとは別物**だからです。

文字列は、`char(文字列)`のようにすることで文字ベクトルに変換できます。
(蛇足: ちなみに`char(文字ベクトル)`は、そっくりそのまま文字ベクトルを返します)
したがって、
`substr(char("hello world"), 2, 6)`
ならOKです。

ただ、これではダサいですよね。
`substr("hello world", 2, 6)`
でもちゃんと動くようにしましょう。

`substr = @(str,i,n) char(str)(i:(i+n-1));`
ならいいでしょうか？

いいえ。
**matlabでは、括弧が隣り合うと、エラーになります**。
したがって、
`substr = @(str,i,n) (c = char(str); c(i:(i+n-1)));`
でいけそうです。
しかしこれもダメです。**無名関数は文を1つしか持たせられない**からです。

何とかして無名関数で書きたい場合、
「`feval`関数」というのを何度も用いることで書けます。

`feval`は、次のような関数です。
「`feval(@f, x) 　`が `f(x)   `を返却する」
「`feval(@g, x, y)`が `g(x, y)`を返却する」
・・・

例1: 
`feval(@cos, 3.14)`は、`cos(3.14)`つまり`-1`を返却します。

例2: 
`feval(@mod, 10,3)`は、`mod(10,3)`つまり`1 `を返却します。

また、
`feval`関数の第1引数には無名関数も持ってこれます。

例3: 
`feval(@(c,i,n) c(i:(i+n-1)), 'hello_world', 2, 6)`
は、`substr('hello world', 2, 6)`つまり`'ello w'`を返します。

ややこしいですが、

```matlab
「'無名関数'内でfevalを利用し、
           そのfeval内で'無名関数'を利用し、
                    その'無名関数'内でfevalを利用し
                                 そのfeval内で'無名関数'を利用し、
                                          その'無名関数'内でfevalを利用し
                                                       そのfeval内で...」
```
のようなことを繰り返すと、複数の文を無名関数で実行できます。
(理由は、「複数の文」全体を、一つの文としてみることが可能になるからです)

いきなり例を示すと混乱するので(もし皆さんが混乱しないとしても、説明している僕のほうが混乱します笑)、

どんな無名関数をつかうか、先に決めましょう。


関数その1: `(s_or_c)` → `c`
引数:
    `s_or_c`: 文字列かもしれないし文字ベクトルかもしれないもの。
戻り値:
    `c`: 確実に文字ベクトル
内容(分かりやすい表現):
`c = char(s_or_c);`
内容(無名関数の書き方で) 
`@(s_or_c) char(s_or_c);`

関数その2: `(c, i, n)` → `answer`
引数:
    `c`
    `i`
    `n`
戻り値:
    `answer`: 得たい部分文字列
内容(分かりやすい表現):
`answer = c(i:(i+n-1));`
内容(無名関数の書き方で)
`@(c, i, n) c(i:(i+n-1));`


では、これを書いてみましょう。

```matlab

substr = ...
(...
    @(s_or_c, i, n) ...
        feval ...                                                            %◆ 
...     %関数その2を無名で定義し、利用します。                               %◆
        (...                                                                 %◆
            @(c, i, n) c(i:(i+n-1)), ...                                     %◆
...         %関数その2を定義                                                 %◆
...                                                                          %◆
            feval ...                             %★                        %◆
...         %関数その1を無名で定義し、利用します。%★                        %◆
            (...                                  %★                        %◆
                @(s_or_c) char(s_or_c), ...       %★                        %◆
...             %関数その1を定義                  %★                        %◆
...                                               %★                        %◆
                s_or_c ...                        %★                        %◆
...             %関数その1を利用                  %★                        %◆
            ), ...                                %★                        %◆
...         %★全体がcとして評価される(＝あたかもcであるかのように振る舞う)  %◆          
            i, n ... %(★=c)と繋げて読むと、c,i,nとなっている。              %◆
...         %関数その2を利用                                                 %◆
        )...                                                                 %◆
...     %◆全体がanswerとして評価される
);
```

こんな感じです。

それでは、失礼いたします。