C#ATIA

↑タイトル詐欺 主にFusion360API 偶にCATIA V5 VBA(絶賛ネタ切れ中)

1つのリストから隣り合う2個の組み合わせリストの取得

あけましておめでとうございます。 気が付けば7年目です。
本年もマイペースで・・・と毎年書いていますが、変わらずやっていきます。

昨年の暮れ、こちらに取り組もうと取り掛かったのですが、ちょっと迷った部分が
ありました。(まだ仕様が不明で、未完成です)
形状に対して等分に点が配置されるコマンドがほしい - Autodesk Community


迷った部分は、タイトルにした "1つのリストから隣り合う2個の組み合わせリストの取得" です。
もうちょっと具体的に説明すると、

lst = ['A','B','C','D','F']

と言うリスト(もしくはセット)から隣り合う2個を組み合わせた

[['A', 'B'], ['B', 'C'], ['C', 'D'], ['D', 'F']]

こんな感じの組み合わせのリスト(又はセット)の作り方です。
正直な所、ちっともFusion360の要素が無くpython的お話です。


VBA風なベタなコードはこんな感じです。

    lst = ['A','B','C','D','F']

    combLst = []
    for idx in range(len(lst)-1):
        combLst.append([lst[idx], lst[idx+1]])

    print(combLst)

頑張って1行にするのであれば

    combLst = [[lst[idx], lst[idx+1]] for idx in range(len(lst)-1)]

インデックスをFor文でぶん回す感じです。まぁ間違いなく機能しています。
・・・オシャレじゃないんです。これ。

pythonでリストの組み合わせや展開は、標準ライブラリのitertoolsが有るので
利用出来ないものか? とちょっと探してみました。
itertools --- 効率的なループ実行のためのイテレータ生成関数 — Python 3.7.9 ドキュメント
ん~理解力不足の為か、思うような物が見つかりません。
あえて挙げるのであればcombinationsかな? とも思うのですが、これでも不要な
組み合わせが多すぎです。

先人の知恵をお借りしたいのが山々なのですが、この様な組み合わせの名称がわからず
検索しようにも適切な言葉がわからない状態。(順列・・・でもないですね)
それっぽそうな言葉でHitしたこちらを見ても、納得が行かないのが正直な所。
Python - pythonでリスト内のリストでの組み合わせ|teratail
結局、itertoolsを断念しました。


pythonのfor文の場合、カウンタを態々用意しなくてもenumerateを利用すると
もれなくカウンタが利用出来ると言う、不思議な仕様は割と知られているかと思われます。
enumerateを利用したコードはこんな感じです。
(本当は、真っ先に思い付いたのはこれです)

    combLst = []
    for idx,v in enumerate(lst[:-1]):
        combLst.append([v, lst[idx+1]])

一行で頑張るとこんな感じです。

    combLst = [[v, lst[idx+1]] for idx,v in enumerate(lst[:-1])]

・・・機能はするけどスッキリしない。正直な所最初と大差ないです。
適切な表現をすると "っぽくない" です。

目的がこの組み合わせリストの作成ではなく、他にも確認したい部分があったため
取りあえずこのままでテストコードの続きを作り、帰宅しました。
(その様な帰り道はスッキリしない)



運良く、翌朝の通勤の運転中に良い方法を思い付きました。(運転に集中すべきです!)
1個のリストで処理させようとするのが悪かったんです。

まず、2項目以降のリストをスライシングで作成します。
f:id:kandennti:20210102215640p:plain
実際は、1個分前に詰まった状態ですね。
f:id:kandennti:20210102215656p:plain
この2個のリストを組み合わせれば良かったんです。
f:id:kandennti:20210102215710p:plain

異なる長さのリストをzip関数に渡した際、短い方のリストの長さまでしか処理しない事も
好都合です。つまり、こんなコードです。
(zipの戻り値がイテレータなのでprint時のList化してます)

def run(context):
    lst = ['A','B','C','D','F']

    combLst = zip(lst,lst[1:]) # <-ここ

    print(list(combLst))

恐ろしい程に清楚。複数行にする方が不自然にすら感じますし、ループすら排除出来ています。

そもそも "1個のリスト" で "次の要素" を取得するためにインデックスを
利用しようとした事が違和感を感じさせていた原因だったようです。(個人的感想)


この様な処理を以前も必要とした場面があったのですが、その時は気が付きませんでした。
これだけで書けるのだからitertoolsに組み込まれないのも納得。
で、この様な組み合わせの名称が未だにわからず・・・今年もよろしくお願いいたします。