与えられた1次元配列のデータのすべての順列組み合わせパターンを計算し、そのとおりに配列を並べて連結した文字列を、すべての順列組み合わせパターンについて生成して返すAppleScriptです。
InDesignやIllustratorなどの書類上に元データをレイアウトした際に、複数のテキストフィールドに分解してレイアウトされていることがあります(見た目重視で)。
その分割されたテキストフィールドを元データと比較する際に、位置情報などをもとに「このあたりが一緒になってそうだ」とあたりをつけるわけですが、左→右、上→下という順序に基づいて配置されているという保証はありません。見た目やインパクト重視で、あえて順番を変えているケースも考えられます。
そこで、並び順について「考えられるかぎりの全ての順列組み合わせでつないでチェック」するという脳筋な(マッチョな)方法を考えてみたりもするわけです。実際、この手の計算にはコンピュータは実に向いている道具なので(やらない方がおかしい?)。
そんな計算を行うために作成したAppleScriptですが、意外や意外、すべての順列組み合わせパターンを計算するルーチンは作ってありませんでした。性格的に、このあたり作ってないと気がすまないと(自分でも)思うものの、これだけ数千本も数万本もAppleScriptを書いておいて、手付かずの処理があったというのは逆に掘り出し物というべきなんでしょう。
そのため、過去に作った一番近いルーチンを流用して、すべての組み合わせパターンを計算し、そのうえで「すべての要素が異なる組み合わせ」だけを抽出しています(すべての組み合わせを計算するのに、「AAAA」とか「AAGG」とかの要素が重複する項目は削除)。
元になっているルーチンは、製品コードなどの複数桁から構成され、それぞれの桁で数値の範囲が異なるという規則性を持つコードを、すべてのパターンについて生成するというものでした。割と、製品カタログ掲載データなどで「ありがち」な仕様です。最新のルーチンでは、各桁についてプロパティ「aRuleList」に、
{下限値, 上限値}
とコードの生成ルールを定義しておいて、そのルールにもとづいてすべてのパターンのコードを展開していました。
出来上がったプログラムにそれほど時間はかけていませんが、それなりの行数に(圧縮記述をせず、見やすく、高度な処理を書けば、行数が増えるのは当然のことなんですけれども)。
「もう少しシンプルに書けそうなもんだけどー?」
という気持ちでいっぱいですが、まずは機能を実現することが重要です。
→ この処理自体を「Permutations」と呼ぶことを見つけ、MacScripter.netにそのものズバリの処理がずらずらと並んでいるのを見て脱力しました。そうかぁ、再帰で処理するのかぁ。あ、全然高速ですね。ただ、項目数を増やすとやっぱりメモリが足りなくなって、処理がきつくなるのは同様の模様で。
→ 結局、MacScripterに投稿された各種Scriptも、最終的にテキスト要素の1次元配列にデータ変換すると、本ルーチンと同じぐらいの処理時間がかかって驚きです 本ルーチン、そんなに遅くない模様(無駄に長いだけで)
AppleScript名:与えられた文字列の1D Listのすべての順列組み合わせパターン文字列を返す v2 |
— Created 2019-06-18 by Takaaki Naganoya — 2019 Piyomaru Software use AppleScript version "2.4" use scripting additions use framework "Foundation" script spd property aList : {} property tmpList : {} property aRuleList : {{1, 4}, {1, 4}, {1, 4}, {1, 4}} –上書きしてしまうので、この通りとはかぎらない property aRuleLen : length of aRuleList end script set targList to {"A", "T", "G", "C"} set aRes to retStrListInAllCombinations(targList) of me –> {"ATGC", "ATCG", "AGTC", "AGCT", "ACTG", "ACGT", "TAGC", "TACG", "TGAC", "TGCA", "TCAG", "TCGA", "GATC", "GACT", "GTAC", "GTCA", "GCAT", "GCTA", "CATG", "CAGT", "CTAG", "CTGA", "CGAT", "CGTA"} –与えた文字列リストを、順列組み合わせの全パターンで組み替え直して返す on retStrListInAllCombinations(targList as list) set aRes to retAllPatterns(targList) of me –> {1234, 1243, 1324, 1342, 1423, 1432, 2134, 2143, 2314, 2341, 2413, 2431, 3124, 3142, 3214, 3241, 3412, 3421, 4123, 4132, 4213, 4231, 4312, 4321} –順列組み合わせデータをもとに元データをその順番に組み合わせる set (tmpList of spd) to {} repeat with i in aRes set jList to characters of (i as string) set tmpStr to "" repeat with ii in jList set tmpStr to tmpStr & (contents of item (ii as integer) of targList) end repeat set the end of (tmpList of spd) to tmpStr end repeat return (tmpList of spd) end retStrListInAllCombinations –与えられた要素数のリストをもとに、すべての順列組み合わせパターンを返す on retAllPatterns(targList) –ルールテーブルの初期化 set targStrLen to length of targList set (aRuleList of spd) to {} repeat with i from 1 to targStrLen set the end of (aRuleList of spd) to {1, targStrLen} end repeat set (aRuleLen of spd) to targStrLen –初版ではここを更新していなかったので、データの桁数を増やしても出力結果の桁数が変わらないというバグがあった — set (aList of spd) to {} –initilaize set initNum to getMinNum() of me –本ルール下における最小値 copy initNum to aNum repeat set aRes to incDigit(aNum, 1) of me if aRes = false then exit repeat end if if (chkEachDigitIsNotSameChar(aRes) of me) = true then if aRes is not in (aList of spd) then set the end of (aList of spd) to aRes end if end if copy aRes to aNum end repeat return (aList of spd) end retAllPatterns –与えられたルール下における最小値をルールリストから求める on getMinNum() –桁数が合っているだけのダミー数字を、適切な桁数作成する(例:11111) set tmpNumStr to "" repeat (aRuleLen of spd) times set tmpNumStr to tmpNumStr & "1" end repeat set tmpNum to tmpNumStr as integer –ルールから各桁の最小値を取り出して、各桁に設定する repeat with i from 1 to (aRuleLen of spd) set aDigNum to item 1 of item i of (aRuleList of spd) set tmpNum to setDigit(tmpNum, i, aDigNum) of me end repeat return tmpNum end getMinNum –繰り上がり処理(再帰呼び出しで使用) on incDigit(aNum, aDigit) set {thisMin, thisMax} to item ((aRuleLen of spd) – aDigit + 1) of (aRuleList of spd) set aTarget to getDigit(aNum, aDigit) of me if aTarget = thisMax then if aDigit = (aRuleLen of spd) then –オーバーフロー(桁あふれ)エラーを返す return false end if set bNum to incDigit(aNum, aDigit + 1) of me if bNum = false then return false set bNum to setDigit(bNum, aDigit, thisMin) of me else set aTarget to aTarget + 1 set bNum to setDigit(aNum, aDigit, aTarget) of me end if return bNum end incDigit –指定数値のうち指定桁の数字を返す on getDigit(aNum, aDigit) set aStr to aNum as string set aLen to length of aStr if aLen < aDigit then return false –エラー end if set tStr to character (aLen – aDigit + 1) of aStr return tStr as integer end getDigit –指定数値のうち指定桁の数字を返す on setDigit(aNum, aDigit, newNum) set aStr to aNum as string set aLen to length of aStr if aLen < aDigit then return false –エラー end if set aList to characters of aStr set item (aLen – aDigit + 1) of aList to (newNum as string) set aaStr to aList as string return aaStr as integer end setDigit –与えた文字列の各キャラクタがすべて「別のもの」であるかをチェック on chkEachDigitIsNotSameChar(aStr as string) set aLen to length of aStr set aList to characters of aStr set bList to {} repeat with i in aList set j to contents of i if j is not in bList then set the end of bList to j else return false –Some duplicated character(s) exists end if end repeat return true –All Characters are different end chkEachDigitIsNotSameChar |
与えられた文字列の1D Listのすべての順列組み合わせパターン文字列を返す v3 – AppleScriptの穴 says:
[…] 合わせた文字列を1次元配列で返すように組んでみたところ、オリジナル版よりは処理時間はかかっていますが、初版からくらべると大幅に処理速度とメモリ使用効率が改善されました。 […]