3
1
paiza×Qiita記事投稿キャンペーン「プログラミング問題をやってみて書いたコードを投稿しよう!」

paizaの問題「N倍の文字列」を例に、Excelワークシート関数をプログラミング的に考えてみた

Last updated at Posted at 2024-08-28

この記事の概要

paiza x Qiitaコラボキャンペーンの問題「N倍の文字列」を、Excelワークシート関数を使って4通りの手法で解き、プログラミング的な観点を加えて考察した。

これを書いたきっかけ

ここ数年でExcelワークシート関数が超強化され、プログラミング言語と呼べるようになったのだが、その割に盛り上がりが少ない。ということで、paiza x Qiitaコラボキャンペーンの問題を使って、Excelのワークシート関数で実装例を紹介してみたする下準備としてワークシート関数について考えてみたら思ったより長くなったので、独立した記事にしてみた。
なお、筆者はプログラミング素人であり、誤り等が含まれている可能性がある。ご意見・ご指摘等は大歓迎である。

ワークシート関数の特徴と使い方を、プログラミングと対比して考える(「N倍の文字列」を例に)

簡単そうなN倍の文字列を例に、ワークシート関数の特徴と使い方について考えてみる。
この問題は初心者にループ構造を使わせるというのがテーマだろうが、残念ながらワークシート関数ではループができない。そのため、ループに代わる何種類かの手法を、ワークシートで実装してみる。

①まずは原始的にやってみる

そもそもExcelとは表計算ソフトである。表計算は、縦や横にセル参照しながら数式で計算し、計算結果をそのまま同じ表に出力する、というのが基本的な使い方になる。まずはプログラミング的な考え方を忘れ、セル参照と数式の概念を知ったばかりのExcel初心者でも解ける方法でやってみる。

解法例

image.png
strの列が主な処理となる。セルの相対参照と数式のコピーを用いて、自らの上のセルに対して1つだけ"*"を追加する。idxが1増えるごとに1行を使って順次処理し、指定されたNに対応する結果が最終出力となる。初期値については最初の行を空白セルとすることで対応した。また、指定されたNになったら処理をやめてその結果を返すことも必要になるが、今回は人力で済ませた。原始的な方法だが、初めてExcelを触ったときはこんなものでも感激していたのではなかろうか。

考察

さて、この原始的な解法だが、図らずも普通のループを使う解法と同じ構造になっている。

Sub N倍の文字列(N As Long) '普通のループを使う解法(VBA)
    Dim idx As Long
    Dim str As String
    str = ""
    For idx = 1 To N
        str = str & "*"
    Next idx
    Debug.Print str
End Sub

つまり、

  • インデックスidxと途中結果を格納する文字列strがある
  • 初期値""に対し1ステップ(1行)ずつ処理をする
  • 処理は前のステップ(上の行)のstrに対して"*"を結合する
  • idxが指定値Nに達したらその時のstrを出力する

という構造である。
ここで気づくさらに重要な点は、

  • プログラミングにおける変数は、ワークシートにおけるセルや列に対応する

ということである。細かい話はともかく、変数の特長である「値に名前を付けて可読性を上げる」「値を再利用できる」「修正時の変更箇所を減らして保守性を上げる」という点は、セルや列でも全く同じである。なお、Excelワークシート数式は関数型言語に分類されるが、その大きな特徴として「変数への再代入はできない(i = i + 1ができない)」がある。Excelをベースに考えて、変数=セルだと思えば、再代入できないのも当たり前に受け入れられる。

この手法のまとめと課題

ワークシートではループはできないが、シートの1行を1ステップとして使えば同等に処理ができるという知見を得た。しかしながら、これでは問題を1つ解くのにシートを1枚使ってしまい、あまりに非現実的である。よって、次は1行でこの処理を実装したい。

②1行でやる

結論から言うと、REDUCE関数を使うことで上記と同じ考えのまま1行で実装できる。

事前説明

REDUCE関数の挙動を説明するため、まずはSCAN関数で上記手法を再現してみる。
最初にSEQUENCE関数を用いてインデックスの配列を作っておく。
image.png
配列は新しい機能(とも最早言えない?)スピルにより複数のセルに返される。また、名前の定義を使ってA2セルにNと名前を付けて可読性を上げている。
つづいて、SCAN関数でstr列を作る。
image.png
構文がやや面倒だが、要するにさっきの1行を1ステップとしたループを回しているようなものである。ここで登場するLAMBDA関数は関数を定義する関数であり、SCAN関数はその関数に配列の要素を1つずつ渡して処理させる(ただし今回は配列の要素数以外の情報は用いていない)。

解法例

本命のREDUCE関数はSCAN関数と構文が同じだが、途中経過は返さず最終結果だけを返す。言語によってはFOLD関数と呼ばれるらしい。
image.png

=LET(
    idx, SEQUENCE(N),
    REDUCE("", idx, LAMBDA(acc,elm, acc & "*"))
)

1行縛りによりインデックスの配列をシートに返せないので、LET関数を使って数式内で変数を定義して、そこに配列を格納している(この場合はLETを使わなくても書けるが)。やっていることは先の2つの手法と同じだが、だいぶスマートになった。

この手法のまとめ

REDUCE関数を使うと、ループや原始的な手法と同じ考えのまま1行で実装できる。

③LAMBDA関数の再帰でやる

LAMBDA関数

SCAN関数のおまけのように書いてしまったLAMBDA関数だが、機能的にはこっちが目玉である。この関数の導入によってExcelワークシート数式がチューリング完全になったのだから、Excel史上最も重要な関数の1つと言っていいだろう。LAMBDAの概念は関数型言語から来ているらしいが、最近ではいろいろな言語でも導入されているらしいので、馴染みある人も多いかもしれない。

再帰を使う

さて、関数型言語の特徴として、LAMBDAで関数を定義し、再帰を使うことで、ループと同様の処理ができるらしい。ということでやってみる。
image.png
NFOLDSTRINGという自作の関数を名前の定義に登録した。ぱっと見はネイティブワークシート関数と変わらずスマートに見える。
関数の中身はこんな感じ。

NFOLDSTRING(N)
=LAMBDA(N,
    IF(N = 0, "", NFOLDSTRING(N - 1) & "*")
)

関数の定義を分かりやすく言うなら
NFOLDSTRING(N) = NFOLDSTRING(N - 1) & "*"ただしNFOLDSTRING(0) = ""
という感じである。
これまでと大きく違うのは、ループでは
「インデックス1からスタートして、Nまで処理したら終わる」だったのに対し、再帰では
「インデックスNからスタートして、1まで処理したら終わる」という書き方になっている点である
(動作としては、Nからスタートして1まで順に自身を呼び出した後、1からスタートして順に値を評価していき、Nに到達したら終わる、という往復運動になる)。
今回の問題ではどちらを使っても大差ないが、もともと再帰的な性質を持つ問題であればこの書き方のメリットがより活きてくることだろう。

この手法のまとめ

問題の種類によっては再帰も重要な選択肢になる。

④既存のワークシート関数でやる

Excelワークシートには多くの関数が用意されている。
image.png

実務ではまず使わないREPT関数である。本当はもっと便利な関数を紹介したかった……

まとめ

  • いくつかの重要なプログラミングの概念は、ワークシート関数にも当てはまる
  • ワークシート関数でも、問題によってはループと同じ考えのまま実装できる
  • 再帰も問題の種類によっては強力に使える
  • ワークシート関数は便利

終わりに

本当はここまでが前置きで、もう少し複雑な問題について書きたかったのだが、想定より長くなってしまったので今回はここで終わりにする。ワークシート数式とプログラミングの関係については、どこかで本腰を入れてまとまった記事を書こうと思う。

3
1
0

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
3
1