【LAMBDA関数】有効桁数で処理する関数を作る【LET関数】
どうも、けーしぃです。数年ぶりの更新です。
仕事柄、有効桁数を指定して処理する作業が多いのですが、エクセルでは桁数を自動で判別して処理する関数は用意されていません。
RoundUp, RoundDown, Round関数は桁数をユーザが指定する必要があるので、1,000円単位と10,000円単位の数字が混ざっていると、それぞれのセルで桁数を確認しないとなりません。
そこで、Microsoft 365、Excel 2021以降で導入されたLAMBDA関数とLET関数で、桁数を自動で判別し、有効桁数をユーザから指定すればいい感じに処理してくれる関数を自作しました。GoogleSpreadsheetでも使えます。
また、LAMBDA関数、LET関数が導入される以前はVBAでユーザ定義関数を作っていましたので、併せて掲載しておきます。
なお、ここで作成した関数は0以上の正の数でしか正しく適用できません。
負の数では切り捨て、切り上げ処理を逆にするなど、少し改良が必要です。
欲しい関数
先にどんな形で一撃処理したいのか考えます。
ユーザの指定が必要な項目は以下のとおりです。
- 対象の数値を指定する ... v
- 処理する桁数を指定する ... keta
- 指定桁の次の桁の処理を指定する ... treat
- 切り捨て -1, 四捨五入 0, 切り上げ 1の3つの値のみ取る
これらを引数としたYukoKetaという関数を作っていきます。
=YukoKeta(v, keta, treat)
たとえば、0.005555を有効桁数3桁、4桁目を四捨五入する場合は次のようになります。
=YukoKeta(0.005555, 3, 0) '有効数字3桁、4桁目を四捨五入(treat = 0) '出力 0.00556
有効桁数の判別
まずは、対象の数字が何桁なのかをLOG10関数とCEILING.MATH関数で判別します。
LOG10関数で対象の数字が10の何乗に近いのか判別し、CEILING.MATH関数(天井関数)で最も近い整数に切り上げます。
CEILING.MATH(LOG10(ABS(v)))
対象の数値をそれぞれの関数で処理した結果は以下のとおりです。

ここで得られた天井関数の数字を用いてRound系関数の桁数を計算します。
'5555555を3桁で四捨五入処理したい場合 =ROUND(5555555, -4) '出力5560000
この-4は、指定桁数-天井関数の出力で計算できます。
=ROUND(5555555, 3-7 = -4)
LAMBDA、LETで組み立てる
上記の関数でYukoKeta関数を組み立てていきます。
先に完成形を示します。改行を消してセルに張り付ければ使用できます。
=LAMBDA(v,keta,treat,
LET(c,CEILING.MATH(LOG10(ABS(v))),k,keta-c,
SWITCH(treat,-1,ROUNDDOWN(v,k),0,ROUND(v,k),1,ROUNDUP(v,k)))
)
(A1,3,0)
'(A1,3,0)より前の部分を YukoKeta という名前で定義しておく(「名前の定義」機能)
'すると、どのセルでもYukoKeta関数が使えるようになる
=YukoKeta(A1,3,0)
LAMBDA関数とLET関数の組み合わせはめちゃくちゃ強力で、最終出力を得るための途中計算をワンライナーに組み込めるようになります。
LAMBDA関数の部分
LAMBDA関数は、無名の関数を作ることができます。(何を言っている)
=LAMBDA(v,keta,treat,LET(...))(A1,3,0) 'A1の手前までが無名の関数 '無名とは、SUM関数やABS関数といったような名前がついていないという意味
プログラミングをやったことがないとなんとも理解しがたいものですが、ごめんなさい進めます。
まずLET関数の中身を無視します。
このv, keta, treatに対応するのが末尾のA1, 3, 0です。
このLAMBDA関数は、3つの引数(ひきすう, v, keta, treatの3つ)を取る関数として定義されています。
LET関数の部分
上記の3つの引数をLET関数に引き渡し、桁数の計算などをしていきます。
'c : 対象の数値の桁数の計算結果を持つ
'k : 指定された桁数-c の計算結果を持つ
LET( c, CEILING.MATH(LOG10(ABS(v))), k, keta-c,
SWITCH( treat, -1, ROUNDDOWN(v,k), 0, ROUND(v,k), 1, ROUNDUP(v,k)))
)
ここもなかなか異様な見た目をしていますが、c = CEILING.MATH(LOG10(ABS(v)))、k = keta-cとして考えてください。
c, kは計算結果を一時的に保持しておく箱のようなもので、LET関数の5つめの引数であるSWITCH関数に引き渡すための数値を保持しています。
SWITCH関数は、1つめの引数であるtreat(A1, 3, 0 ののうち、0が入る)の値に応じて処理を分岐させる関数ですが、-1, 0, 1の3つの処理を行っています。
今回はゼロが入力されているため、ROUND(v,k)の結果が出力される形となります。
もし-1, 0, 1以外であれば、エラー値として#N/Aが出力されます。
名前の定義で登録する
上記完成形をYukoKetaという名前で登録します。

スクショはGoogle Spreadsheetですが、エクセルでも「名前の定義」で同じように登録できます。
これで、=YukoKeta(A1, 3, 0)というような形で関数呼び出しが可能になります。
VBAで組み立てる
VBAで関数を定義する場合は下記のとおりです。標準モジュールを作成して貼り付ければ完了です(急にぶん投げ)。
なお、マクロ有効化ファイルとするのが必要なので、エクセルファイルの保存時に.xlsx⇒.xlsmに拡張子を変更して保存してください。
'value: 処理する値
'keta : 有効桁数の指定
'treat: 1=次の位切上、0=次の位四捨五入、-1=次の位切捨
'正の値の切上げは+∞方向への丸め、切下げは-∞方向への丸め
'負の値の切上げは-∞方向への丸め、切下げは+∞方向への丸め
Public Function YukoKeta(ByVal value As Double, ByVal keta As Integer, ByVal treat As Integer) As Double
If value = 0 Then
YukoKeta = 0
Exit Function
ElseIf keta < 1 Or Abs(treat) >= 2 Then
YukoKeta = Error(5)
Exit Function
End If
Dim ceil As Long
'先頭の数字がどこにあるか
ceil = -Application.WorksheetFunction.Ceiling_Math(Application.WorksheetFunction.Log10(Abs(value)))
If treat = 1 Then
'指定桁の次を切上げ
YukoKeta = Application.WorksheetFunction.RoundUp(value, keta + ceil)
ElseIf treat = 0 Then
'指定桁の次を四捨五入
YukoKeta = Application.WorksheetFunction.Round(value, keta + ceil)
Else
'指定桁の次を切下げ
YukoKeta = Application.WorksheetFunction.RoundDown(value, keta + ceil)
End If
End Function
まとめ
LAMBDA関数とLET関数はプログラミングの機能が関数として切り出されたような存在で、それゆえ使い方が難しいですがめちゃくちゃ強力な関数です。
それぞれ単体で解説されている記事は数多ありますが、組み合わせるとこんなことができるという記事があまりなかったので書いてみました。
プログラミングを勉強されたことがある方は、VBA無しでいろいろな関数を作ることができるかと思います。
AndroidでMiAuth実装してみた
リポジトリ
AndroidでMiAuthを実装しました。
手っ取り早くソース寄こせって人はこちらから。 developmentブランチです。
あと、かなりクラスを行ったり来たりするので、コード表示しながら読んでいただければ幸いです。
Misskey Hub
解説では明示されていませんが、カスタムスキーマは現在受け付けておらず、httpsに統一しているそうです(しゅいろママが言ってた)。
トークン取得フロー
MiAuthは、次のようなイメージでアクセストークンを取得します。 OAuth2は未実装なので、詳細については言及しません。

さらに詳細を文字起こしにするとこんな感じ。
(一応MiAuth想定のフロー) 認証開始 -> ViewModel#startAuth -> AuthUseCase#startAuth -> ユーザによりアプリ連携が許可される -> Activity#onNewIntentでコールバックURLのキャッチ -> RegisterAppCallback#onRegistered で受信する -> Interactor#requestToken -> トークンが返ってくる(成功の場合) -> ViewModel で実装し Interactor に渡されている AuthResultCallback を叩いてトークンを ViewModel に渡す -> ViewModel から PreferenceUseCase 等を通じてトークンを保存する
インターフェース定義
AuthActivityとAuthActivityViewModelは認証フローの詳細を知らず、認証サーバとのやりとりはすべてAuthUseCaseを実装した各種インタラクタが実行します。
ViewModel はほとんどの場合、Activityからのイベントをインタラクタに通知するだけです。
また、MVVM を採用しているので、ViewModel は各種 UseCase を集約しています。
AuthUseCase
MiAuth以外にも対応できるようにsealed interfaceで定義しました。
sealed interfaceとすることで、AuthUseCaseを次のようにobject句で実装しようとしてもエラーになります。
また、どんな認証フローでもstartAuthを叩くことでフローを開始するように縛ります。
fun foo() { val XxxAuthInteractor = object : AuthUseCase { ... } } //-> This type is sealed, so it can be inherited by only its own nested classes or objects
AuthUseCaseは次のとおり定義します。
sealed interface AuthUseCase { fun startAuth(context: Context, ticket: AuthTicket, resultCallback: AuthResultCallback): RegisterAppCallback fun onDismiss() interface MiAuthUseCase: AuthUseCase interface OAuth2UseCase: AuthUseCase } //ユーザがアプリ連携を許可した際に必要なコールバック //フローにActivity#onNewIntentが絡むため、何かしらの形でインタラクタに通知する仕組みが必要 interface RegisterAppCallback { suspend fun onRegistered(callbackIntent: Intent) suspend fun onFailed(err: Exception) } //トークンをリクエストした際のサーバのレスポンス //取得に成功した場合、tokenが格納される sealed interface AuthResultCallback { fun onAuthFinish(ticket: AuthTicket, token: Token) fun onFailed(err: Exception) interface MiAuthResultCallback: AuthResultCallback interface OAuth2ResultCallback: AuthResultCallback }
AuthTicketは各種パーミッションやホスト情報等をまとめた単なるデータクラスです。
こちらもsealedで定義します。
MiAuthInteractor と OAuth2Interactor
AuthUseCaseを実装した具象クラス。
このように認証方式ごとにAuthUseCaseを実装したクラスを作ることで、startAuthを叩くだけで異なる認証フローを起動できるようにしています(たぶん)。
また、RegisnterAppCallbackは Interector#startAuth にて、AuthResultCallback は ViewModel にて実装しています。
MiAuthInteractor github.com
AuthActivityViewModel github.com
雑なまとめ
- 一部甘い部分(クラス変数にしている部分とか)があるけどそこはスルーで。
- 別にMiAuthでトークン取得するテストなだけならここまでやる必要はない。。。
まとメモ プライバシーポリシー
まとメモ プライバシーポリシー
第三者に個人を特定できる情報を提供することはありません。
個人情報の管理には最新の注意を払い、以下に掲げた通りに扱います。
サポート時
サポートメールに、問題解決のための端末種類、OSバージョン等が本文として記述されます。
個人を特定できる情報は一切送信されません。
データ解析
アプリの利便性向上のため、匿名で個人を特定できない範囲で最新の注意を払い、アクセス解析をしております。
例えばアプリのクラッシュ時にどんな原因でクラッシュしたかを匿名で送信して、バグの素早い修正に役立たせております。
免責事項
利用上の不具合・不都合に対して可能な限りサポートを行っておりますが、利用者が本アプリを利用して生じた損害に関して、開発元は責任を負わないものとします。
CoordinatorLayout+BottomNavigationView+NestedScrollView+その他諸々で画面構築してみた
次のぎじゅつを使って画面構築してみたのでメモ
- 見た目はToolbar+コンテンツ表示Fragment+BottomNavigationViewの画面構成
- コンテンツ表示Fragmentは、BottomNavigationViewの選択により入れ替える
- Toolbarはコンテンツ表示Fragmentのスクロール動作により隠れるようにする
- Toolbarのタイトルを、Navigationを使ってBottomNavigationViewに連動させる
- BottomNavigationViewは固定する
BottomNavigationView & Toolbar & Navigation 間の連携に微妙なコツがあったり、 そもFrameLayoutであることを考慮したり、いろいろハマりまくった。
スクロールでBottomNavigationViewも隠したい場合は、カスタムBehavior定義して設定してやればおけ。 個人的には画面切り替えにスクロール動作が増えてしまうので、あえて固定する方法をとった。
- 動作プレビュー
- レイアウトXML
- 実装編
- NavigationとToolbar/BottomNavigationViewの連携の実装
- コンテンツのレイアウトを作成する
- BottomNavigationViewのMenuを作成する
- Navigation graphを作成する
- FragmentContainerViewにnav_graphをセット
- スクロール時にToolbarを隠す動作の実装
- 基本となる画面構造
- NestedScrollView内にRecyclerViewを配置する場合の注意
- NavigationとToolbar/BottomNavigationViewの連携の実装
- 参考