Archive for the '内部計算(calc)' Category

02/03 近似式によるRGB→CMYK/CMYK→RGB変換

AppleScriptでRGBの色データをCMYKの色データに変換する手段といえば、Color Sync Scirpingを用いるのが常套手段だったのですが、これがMac OS X 10.6で廃止になりました(残念!)。

・OS X 10.7, LionのRelease Noteに書かれていない変更点
http://piyocast.com/as/archives/1751

ほかに、色データの変換を行う手段といえば、安直にPhotoshopに計算してもらうというものもあります。こちらは、現在でも(Photoshopが入っている環境であれば)利用できます。

・与えられたテキストをPhotoshopでRGBデータと評価してCMYK値を返す
http://piyocast.com/as/archives/1898

ほかには、AppleScriptObjCが使えれば、NSColorのgenericCMYKcolorSpaceを用いて変換することも可能です。genericなCMYKcolorSpaceだけでなく、出力デバイス名を指定して出力特性を反映させて変換を行うことも可能です(genericしか試してないけど)。

……で、これらの手段が使えない場合にどうするかといえば、RGB→CMYK/CMYK→RGBの変換を行う「近似式」があるため、それを使うのが一般的です。

・ISP imaging-developers-色変換式集 - CMYK
http://image-d.isp.jp/commentary/color_cformula/CMYK.html

この近似式を用いた(Objective-Cで書かれた)プログラムの処理結果が思わしくないということだったので、AppleScriptで近似式を用いて相互変換の処理を書いてみました。

RGB→CMYK変換では、黒に近い色(というか黒)を与えると0による除算が発生したり、マイナスの値になったりするケースが見られたため、try〜end tryでエラーをキャッチしたりするなど、地道な補正を加えています。

本Scriptでは、choose colorコマンドで指定した色(RGB)を一度CMYKに変換し、変換したCMYK値を再度RGB値に変換してchoose colorコマンドで色プレビューを行っています。

RGB→CMYKの変換時に発生する一種の「くすみ」がなく、見たまんまそのまま変換されてしまうので、分る人が見るとウソだと分るのですが、単に変換したいだけという場合には割と使えるという感触です。

スクリプト名:指定色をRGB→CMYK→RGB変換プレビュー
–色選択
set aCol to choose color

set {rNum, gNum, bNum} to aCol

set rNum to rNum div 255
set gNum to gNum div 255
set bNum to bNum div 255

–RGB→CMYK変換
set {cNum, mNum, yNum, kNum} to approximateRGBtoCMYKconvert(rNum, gNum, bNum) of me

–CMYK→RGB変換
set {rNum2, gNum2, bNum2} to approximateCMYKtoRGBconvert(cNum, mNum, yNum, kNum) of me

set rNum2 to 65535 * rNum2
set gNum2 to 65535 * gNum2
set bNum2 to 65535 * bNum2

–色プレビュー
choose color default color {rNum2, gNum2, bNum2}

–近似式ベースのRGB→CMYK変換
–参照資料:
–http://image-d.isp.jp/commentary/color_cformula/CMYK.html
on approximateRGBtoCMYKconvert(r, g, b)
  
  
set rNum to r / 255
  
set gNum to g / 255
  
set bNum to b / 255
  
  
set nList to {1 - rNum, 1 - gNum, 1 - bNum}
  
  
set kNum to minimumFromList(nList) of me
  
  
–kNum(Black)がマイナスになるケースがあるため、値を補正
  
if kNum < 0 then set kNum to 0
  
  
  
–0による除算が発生した場合の算術エラーに対処
  
try
    set cNum to (1 - rNum - kNum) / (1 - kNum)
  on error
    set cNum to 0
  end try
  
  
–0による除算が発生した場合の算術エラーに対処  
  
try
    set mNum to (1 - gNum - kNum) / (1 - kNum)
  on error
    set mNum to 0
  end try
  
  
–0による除算が発生した場合の算術エラーに対処  
  
try
    set yNum to (1 - bNum - kNum) / (1 - kNum)
  on error
    set yNum to 0
  end try
  
  
  
–C/M/Yがマイナスになるケースがあるため、値を補正
  
if cNum < 0 then set cNum to 0
  
if mNum < 0 then set mNum to 0
  
if yNum < 0 then set yNum to 0
  
  
  
return {cNum, mNum, yNum, kNum}
  
end approximateRGBtoCMYKconvert

–近似式ベースのCMYK→RGB変換
–参照資料:
–http://image-d.isp.jp/commentary/color_cformula/CMYK.html
on approximateCMYKtoRGBconvert(c, m, y, k)
  
  
set rNum to 1 - (minimumFromList({1, c * (1 - k) + k}))
  
set gNum to 1 - (minimumFromList({1, m * (1 - k) + k}))
  
set bNum to 1 - (minimumFromList({1, y * (1 - k) + k}))
  
  
return {rNum, gNum, bNum}
  
end approximateCMYKtoRGBconvert

–最小値を取得する
on minimumFromList(nList)
  script o
    property nl : nList
  end script
  
  
set min to item 1 of o’s nl
  
repeat with i from 2 to (count nList)
    set n to item i of o’s nl
    
if n < min then set min to n
  end repeat
  
return min
  
end minimumFromList

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

09/04 簡単な素因数分解v2

簡単な素因数分解を行うAppleScriptのバージョン2です。

バージョン1では素因数分解を行うだけでしたが、バージョン2では約数を同時に返すようにしました。

素因数分解を行っただけでは、構成要素となる素数がバラバラに取得できるだけで、約数については素因数分解を行った要素から総当たりで作る必要があり、そのあたりがちょっとお手軽に結果が得られない雰囲気。

そこで、素因数分解を行う最中に出てきた数値を別途記録しておくことで、それっぽい約数が得られて一石二鳥。

ただし、だんだん素因数分解を行うことが目的化してきたことに気付いて、あわてて本来の問題解決のためのプログラムを別途作ることに…………。

スクリプト名:簡単な素因数分解v2
set a to 64
set {b1List, b2List} to returnDivisor(a) of divisorKit
–> {{2, 2, 2, 2, 2, 2}, {32, 16, 8, 4, 2}}

–簡単な素因数分解
script divisorKit
  on returnDivisor(a)
    –1000以下の素数のリスト (prime number list under 1000)
    
set primeNum1000 to {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997}
    
    
if a > 1000 then return false –1000より大きかったらfalseをリターン(計算範囲外エラー)
    
    
if a is in primeNum1000 then
      return true –1000までの数字で、素数だった場合
    end if
    
    
–ここからメイン処理
    
set divisorList to {} –約数のリスト
    
set ansList to {} –元の数を約数で割ったときの商のリスト
    
    
repeat while a > 0
      set {r1, r2} to returnDivisorSub(a, primeNum1000) of me
      
if r1 = false then exit repeat
      
      
set the end of divisorList to r1
      
      
–計算結果リストに1が入ることを防ぐ
      
if r2 is not equal to 1 then
        set the end of ansList to r2
      end if
      
      
set a to r2
    end repeat
    
    
return {divisorList, ansList}
    
  end returnDivisor
  
  
  
on returnDivisorSub(a, primeNumList)
    repeat with i in primeNumList
      set a1 to a div i
      
set a2 to a / i
      
if a1 = a2 then
        return {contents of i, a1}
      end if
    end repeat
    
return {false, false}
  end returnDivisorSub
end script

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

09/02 簡単な素因数分解

簡単な素因数分解を行うAppleScriptです。

簡単な……というのは、1000までの数に処理対象を限定しているのと、あんまりしっかり検証していないので「あ、動くからいいや」ぐらいの完成度のためです。実務で長年ビシバシにいじめまくって、これなら大丈夫……というほど裏がとれているわけでもない、というレベルです。

そもそも、なんでまた素因数分解などを作ろうと考えたかといえば、InDesign上のオブジェクトにスクリプトラベルをつける作業をしていたときに、最近こういう作業はぜんぶAppleScriptから自動で採番して実行するのですが、ラベルを付ける対象が数百個にも達し、しかもそれらが整然と並んでいるため、

 「縦に何個並んでいるか、全体の個数から勝手に判断してくれたらいいのに〜!」

などと、ふと思ってしまったからで、「じゃあ、要素数を素因数分解して、適当な候補数をchoose from listで表示してくれたらいいな〜」などと、あまり考えずに勢いだけで作りはじめたのがきっかけです(長いな)。

素因数分解などとおおげさなことを言っても、しょせんは素数で割ったリストにすぎないので、1から1000までの素数のリストを取り寄せて、割り算しながらループを回すというお気楽仕様に。思い立ってから出来上がるまで、10分ぐらい。

これで結局役に立たなくて、version 2を作る羽目になるのですが……。

スクリプト名:簡単な素因数分解
set a to 312
set b to returnDivisor(a) of divisorKit
–> {2, 2, 2, 3, 13}

–簡単な素因数分解
script divisorKit
  on returnDivisor(a)
    –1000以下の素数のリスト (prime number list under 1000)
    
set primeNum1000 to {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997}
    
    
if a > 1000 then return false –1000より大きかったらfalseをリターン(計算範囲外エラー)
    
    
if a is in primeNum1000 then
      return true –1000までの数字で、素数だった場合
    end if
    
    
–ここからメイン処理
    
set divisorList to {}
    
    
repeat while a > 0
      set {r1, r2} to returnDivisorSub(a, primeNum1000) of me
      
if r1 = false then exit repeat
      
      
set the end of divisorList to r1
      
      
set a to r2
    end repeat
    
    
return divisorList
  end returnDivisor
  
  
  
on returnDivisorSub(a, primeNumList)
    repeat with i in primeNumList
      set a1 to a div i
      
set a2 to a / i
      
if a1 = a2 then
        return {contents of i, a1}
      end if
    end repeat
    
return {false, false}
  end returnDivisorSub
end script

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

07/13 数値の序数の文字列を返す

数値を渡すと、英語の序数(1st, 2ndなど)の文字列を返すAppleScriptです。

あまり面白味も何もないのですが、必要に迫られて作成したものです。1->1st、11->11th、21->21st……と、11,12,13の時だけ例外処理を行っています。

AppleScriptのsayコマンドに「11st」などと間違った序数を渡しても、「11th」と言い換えたりしているので、OS内部には序数の処理機能が入っているはずですが、ASからは利用できません。

単なるif文の塊になっていますが、まあこんな感じではないでしょうか。

スクリプト名:数値の序数の文字列を返す
repeat with i from 1 to 100
  set a to retOrdinalNumStr(i) of me
  
log a
end repeat

–数値を与えると序数の文字列を返す
on retOrdinalNumStr(aNum)
  set aStr to aNum as string
  
  
–下1桁の数字を取得
  
set last1Str to last character of aStr
  
  
–下2桁目の数字を取得
  
if length of aStr > 1 then
    set last2Str to character -2 of aStr
  else
    set last2Str to ""
  end if
  
  
–場合分け
  
set retStr to ""
  
if last1Str = "1" then
    if last2Str = "1" then
      set retStr to "th" –11
    else
      set retStr to "st"
    end if
    
  else if last1Str = "2" then
    if last2Str = "1" then
      set retStr to "th" –12
    else
      set retStr to "nd"
    end if
    
  else if last1Str = "3" then
    if last2Str = "1" then
      set retStr to "th" –13
    else
      set retStr to "rd"
    end if
    
  else
    set retStr to "th"
    
  end if
  
  
return aStr & retStr
  
end retOrdinalNumStr

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

11/28 変数値のスワップ

複数の変数の内容を入れ替えるAppleScriptサンプルです。

一度、他の変数に値を退避させて順次代入……などと回りくどいことをしなくても、一度に指定すればシンプルに記述できます。

InDesignドキュメントを2枚オープンして、ウィンドウの座標値からどちらが画面上の左に位置していて、どちらが右に位置しているかを検出。左側ドキュメントから右側ドキュメントにScript Labelを手がかりにデータを転送するAppleScriptを書いていたときに、これらの左右を入れ替える機能を実装する羽目になって、そういえば変数値のスワップって面倒かもしれない……などと思いながら、1行で書いてみたらたいへんにシンプルな記述で済んだ……ということがありました。

その時には変数にはInDesignのLayout Windowオブジェクトへの参照が入っていたわけですが、このような一番かんたんなレベルに立ち返ってその場で試してみて、挙動を確認しつつ実際のプログラムに反映させました。記述サンプルとしては、難しくて複雑なプログラムよりも、簡単で分りやすくて短いものの方が役立つことが多いように感じています。

スクリプト名:変数値のスワップ
set a to 1
set b to 2

set {a, b} to {b, a}
–> {2, 1}

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

ちなみに……2つ以上の変数の場合でも、とくに問題はありません。

スクリプト名:変数値のスワップ2
set a to 1
set b to 2
set c to 3

set {a, b, c} to {c, b, a}
–> {3, 2, 1}

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

11/25 擬似的にPropertyファイルを使ってカウントアップ

保存ファイルを~/Library/Preferencesに作成し、カウントアップを行います。

カウンター値を保持する場合には、propertyでScript中に保持しておくのが一番簡単ですが、AppleScriptのプログラムを書き換えてしまうと初期値に戻ってしまいます。そこで、初期設定ファイル(もどき)を作成して、リストの値をそのまま保存。こうしておけば、プログラムを書き換えて実行しても、カウント内容が維持されます。

本物のplistファイルで値を保持してもいいのですが、より簡単かつ安直にファイルに書き込んで使ってもよいだろう、というアプローチです。

スクリプト名:Propertyファイルを使ってカウントアップ
set aRes to countUp(“jp.piyomarusoft.counter.pref”) of countUpKit

script countUpKit
  –カウントアップ
  
on countUp(anIDentifier)
    set apDir to (path to preferences folder from user domain) as string
    
set a to apDir & anIDentifier
    
tell application “Finder”
      set fRes to exists of file a
      
if fRes = false then
        –初期設定ファイルがなければ初期化(1からカウントアップし直し)
        
write_to_file_AsList({1}, a) of me
        
return 1
      else
        set aCount to read file a as list
        
set aaCount to (item 1 of aCount as number) + 1
        
write_to_file_AsList(aaCount, a) of me
        
return aaCount
      end if
    end tell
  end countUp
  
  
–ファイルの追記ルーチン「write_to_file_AsList」  (リストとして書き込み)
  
–データ、対象ファイル
  
on write_to_file_AsList(this_data, target_file)
    try
      set the target_file to the target_file as text
      
set the open_target_file to open for access file target_file with write permission
      
set eof of the open_target_file to 0
      
write this_data to the open_target_file starting at eof as list
      
close access the open_target_file
      
return true
    on error error_message
      try
        close access file target_file
      end try
      
return error_message
    end try
  end write_to_file_AsList
end script

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

09/19 Sin Cos演算ルーチン

三角関数のサイン、コサインを演算するサブルーチンです。海外でひろってきました。

先日、知人から「AppleScriptで三角関数を求めるための命令はないのか?」と聞かれたのですが、標準ではAppleScriptの処理系に三角関数を求める命令は入っていません。

サードパーティのOSAX(Scripting Additionとも呼ばれる)を見つけてきて、プログラミングを行うMacの環境および実行するMacの環境に同じOSAXをインストールしておく必要があります(バンドル形式のアプリケーションにして、バンドル内にOSAXを入れておくという手もあります)。

実際、仏Satimage Softwareが配布している「Satimage」OSAXにはSin/Cosをはじめとする数値関数の命令が入っています。たいていの場合にはこれを利用するのがよいでしょう。

ただし、どうしても実行環境にOSAXをインストールしたくないとか、どこで実行しても大丈夫なようにしておきたいという場合には、このような三角関数演算ルーチンを作ったり拾ってきたりすることになることでしょう。

または、たいてい近似値で済む場合が多いですし、パラメータに整数しか渡さないといった条件が整っている場合には、360度分のSinテーブル、Cosテーブルを作成しておいて、そこから求めるという手もあります。

以前に、Aboutメニューでいろんな文字が画面上を楕円運動するようなAppleScript Studioのアプリケーションを作ってみましたが、その時にはこのSin/Cosテーブルを用意して実装した次第です。

スクリプト名:Sin Cos演算ルーチン
set a to sine_of(47) of me
–> 0.731353701619

set b to cosine_of(71) of me
–> 0.325568154457

on sine_of(x)
  repeat until x 0 and x < 360
    if x 360 then
      set x to x - 360
    end if
    
if x < 0 then
      set x to x + 360
    end if
  end repeat
  
  
set x to x * (2 * pi) / 360 –convert from degrees to radians
  
  
set answer to 0
  
set numerator to x
  
set denominator to 1
  
set factor to -(x ^ 2)
  
  
repeat with i from 3 to 40 by 2
    set answer to answer + numerator / denominator
    
set numerator to numerator * factor
    
set denominator to denominator * i * (i - 1)
  end repeat
  
  
return answer
end sine_of

on cosine_of(x)
  return sine_of(x + 90)
end cosine_of

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

01/19 変数やサブルーチン名称に日本語を使用する

変数とサブルーチン名称、およびレコードの属性名については、前後をパーティカルバー(パイプ)記号で囲むことで、日本語の文字列が使えます。
(more…)

01/11 pdfinfoの結果からポイント表記の数値をmmに書き換える

xpdf-toolsに含まれるpdfinfoを使ってPDFの情報を取得したあとに、サイズがポイント表記になっているものをmmに書き換えるサブルーチンです。汎用性も見るべきポイントもありませんが、まーこんなもんでしょう。
(more…)

08/05 データが数値かどうか調べる

渡されたデータが数値か、あるいは数値として評価可能な文字列かを調べるルーチンです。割と使っている気もするのですが、いつも作り捨てしていたようなので、掲載してみました。

例によって、クラスを取得したあとにすぐに文字列にしていますが、これはAppleScript Studio環境内でまともに動くように対策しているものです(classの値を取得しただけではわけの分らない数値になってしまうため)。

スクリプト名:データが数値かどうか調べる
set aNum to "300"
set aRes to numChk(aNum) of me
> true

数値かどうか調べる
on numChk(aNum)
  set aClass to (class of aNum) as string
  
if aClass = "number" or aClass = "double" or aClass = "integer" or aClass = "real" then
    return true
  else if aClass = "string" or aClass = "text" or aClass = "unicode text" then
    try
      set bNum to aNum as number
      
return true
    on error
      return false
    end try
  end if
end numChk

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

08/05 指定の数値リストを作成する

ループ内で、イレギュラーな処理をしたいような場合……たとえば、ループカウンタが奇数のときだけ処理を行いたいが、20のときにも例外的に処理を行いたい……といった場合、まあ普通はif文で条件分岐するわけですが、これが増えた場合には大変なことになります。
(more…)

07/30 小数点以下を指定桁で切り捨て

数値を指定桁で切り捨てる(roundする)サブルーチンです。絶対にいつかどこかで作ったはずなのに、見つからなかったのでその場で作ってしまいました。

InDesign CS3で表を作るときに、単純に作成予定サイズを行数で割った数を使うだけでは誤差が出てしまったので、割った数をこのように任意桁で切り捨てて処理してみました。それでも計算が合わなかったので、現実的な解決方法(やや仕上がりサイズを小さくしておく)で帳尻を合わせましたが……。

スクリプト名:小数点以下を指定桁で切り捨て
set a to 3.14159265359
set aRes to roundWithSpecifiedDigit(a, 2) of me
> 3.14

小数点以下を指定桁で切り捨て
on roundWithSpecifiedDigit(aNum, aDigit)
  set bNum to aNum * (10 ^ aDigit)
  
set cNum to round bNum rounding down
  
set dNum to cNum / (10 ^ aDigit)
  
return dNum
end roundWithSpecifiedDigit

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

07/28 奇数かどうかチェック

奇数かどうかをチェックするサブルーチンと、偶数かどうかのチェックを行うサブルーチンの詰め合わせです。

ある意味、そこまで細かい機能モジュールをサブルーチン化しておく意味があるのかどうか、その限界のような気もしますが……使い回しを促進することを考えるなら、このぐらいの機能モジュールがサブルーチンとして存在していてもいいのかもしれません。

スクリプト名:奇数かどうかチェック
set a to 5
set aRes to chkOddNum(a) of me
> true

set b to 8
set bRes to chkEvenNum(b) of me
> true

奇数かどうかチェック
on chkOddNum(aNum)
  set a to aNum mod 2
  
if a = 1 then
    return true
  else
    return false
  end if
end chkOddNum

偶数かどうかチェック
on chkEvenNum(aNum)
  set a to aNum mod 2
  
if a = 0 then
    return true
  else
    return false
  end if
end chkEvenNum

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

07/03 uuidgenでユニークな(重複のない)文字列を取得

一時的な作業ファイルをどこかに書いておきたい……といった場合、Classic Mac OSの時代にはDesktop FolderやHDDのトップディレクトリに書くことが多かったように思います。ひとつには、パス名が長くなると正常にアクセスできなくなるので、なるべく「浅い」場所に置いておく必要があった、といった理由だったでしょうか(すでにClassic Mac OSを使わなくなって久しいため忘れました)。

Mac OS Xになってからというもの、temporary items folder内に作業フォルダを一時的に掘って使うというスタイルが定着し、system domainのtemp folderでもuser domainのtemp folderでも、思い思いに好きに使っている印象を受けています(10.5からはデフォルトのtemporary items folderがuser domainのものになりました)。

temporary items folder内に一時ファイルを書き込むにしても、ファイル名の衝突がないかどうかを確かめてからぼちぼち書き込むわけで、そのあたりは少々まだるっこしい(めんどくさい)処理を書かなくてはなりません。まあ、一度書けば別に使い回すのでいいんですが……かったるいことこのうえありません。

そこで発想の転換……ファイル名の重複におびえてチェックしまくるのではなく、そもそも「重複しないファイル名」で書いておけばチェックなんて必要ないんじゃないか、と。

そもそも、UNIXのコマンドにそれを意図したとおぼしきuuidgenコマンドが用意されており、一時ファイルを書き込むときには、uuidgenによって得られた文字列をファイル名にしていたり、uuidgenの文字列に日付の文字列を連結して使ってみたりしています。短い記述ですが、おぼえておくと非常に有用な処理です。

スクリプト名:uuidgenでユニークな文字列を取得
set uniqueName to (do shell script "/usr/bin/uuidgen")

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

06/26 真偽値の反転

真偽値(true/false)を反転させる演算は、フラグ処理などで割とよく使います。また、タイマー割り込みの許可/禁止などでも使ったりします。

AppleScript/AppleScript Studioでもidleハンドラによるタイマ割り込み処理が行えるため、かなり凝ったことができたりするわけですが、メインの処理を行っている間にはタイマ割り込みサービスを行ってほしくないケースなども多々あります。

そんなときに、booleanで変数内に保持しているフラグの内容をnotで反転させるとか、まあそんなところでしょうか。ちなみに、AppleScriptの変数は型がとくにないうえに宣言もいらないので(代入文を実行した時点で勝手に作られる)、booleanを代入している変数に対して文字列とかアプリケーションのオブジェクトを突っ込むといったこともできます。業界的には「やっぱり変数の型にはある程度の決まりがあったほうがミスを誘発しなくていいよね」という感じですが、AppleScriptは意外と歴史が長い(1994年生まれ)のでこんな仕様になっています。

スクリプト名:真偽値の反転
set a to true

set b to not a
> false

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

04/03 10進数をn進数文字列に変換する

非常に基本的な処理で、16進数変換などはOSAX(スクリプティング拡張書類)をダウンロードしてきてAppleScriptの命令を拡張して使うことが多いようですが、そういう便利なものに慣れてしまうと、イザOSAXを一緒に配布できないようなケース(AppleScript Studioでアプリケーションを作って配布するような場合)には困ります。Mac OS 9の時代はOSAXを活用していましたが、Mac OS Xの時代になってからというもの、OSAXを極力使わずに自前で処理を記述するケースが圧倒的に増えてきました。

16進数のほか、Excelのカラムは26進数ととらえられますし、いろいろ活躍する場は多い処理です。

スクリプト名:10進数をn進数文字列に変換する
set a to 500
set b to aNumToNthDecimal(a, 16, 4, {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"}, true) of me
b
> "1F4"

10進数をn進数文字列に変換する

origNum:変換対象の数字
nTh: n進数のn
stringLength: 出力指定桁(上位桁のアキはゼロパディングされる)
zeroSuppress: 上位桁がゼロになった場合に評価を打ち切るかのフラグ、ゼロパディングを行わない(ゼロサプレスする)場合に指定
on aNumToNthDecimal(origNum, nTh, stringLength, stringSetList, zeroSuppress)
  set resString to {}
  
repeat with i from stringLength to 1 by -1
    if {origNum, zeroSuppress} = {0, true} then exit repeat
    
set resNum to (origNum mod nTh)
    
set resText to contents of item (resNum + 1) of stringSetList
    
set resString to resText & resString
    
set origNum to origNum div nTh
  end repeat
  
return (resString as string)
end aNumToNthDecimal

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

03/19 文字列の長さでソートする

文字列で構成されたリストを、各文字列の長さに着目してソートするサブルーチンです。
(more…)

03/18 指定文字列を指定バイト数以内に収める

指定文字列を指定バイト数以内に収まるように短くします。ただ、Mac OS X 10.5上では(AppleScriptの内部処理コードが完全にUnicodeになったため)正しい結果を返さないはずです。

スクリプト名:指定文字列を指定バイト数以内に収める

set aText to高k甲o子u園Koshien
set aRes to trimTextInByte(aText, 7) of me

指定文字列を指定バイト数以内に収める
on trimTextInByte(aText, aByte)
  set aText to aText as stringSJISで処理するので、Unicodeの2バイト以上の文字のことは気にしない
  
  
set aList to characters of aText
  
  
set dtPath to (path to temporary items folder from user domain) as string
  
set outPath to dtPath & “.strtemp.txt
  
set inPath to POSIX path of outPath
  
  –
文字のバイト数テーブルを作成する
  
set lenList to {}
  
repeat with i in aList
    set j to contents of i
    
write_to_file(j, outPath, false)
    
set hexT to do shell scripthexdump ” & quoted form of inPath
    
set pList to paragraphs of hexT
    
set lastNumText to (last item of pList) as number
    
set the end of lenList to lastNumText
  end repeat
  
  –
文字列の切り捨て処理
  
set loopCount to 1
  
set totalByte to 0
  
set hitF to false
  
repeat with i in lenList
    set totalByte to totalByte + i
    
if totalByte = aByte then
      set resText to items 1 thru loopCount of aList
      
set resText to resText as string
      
set hitF to true
      
exit repeat
    else if totalByte > aByte then
      set resText to items 1 thru (loopCount - 1) of aList
      
set resText to resText as string
      
set hitF to true
      
exit repeat
    end if
    
set loopCount to loopCount + 1
  end repeat
  
if hitF = false then return “”
  
  
return resText
  
end trimTextInByte

on write_to_file(this_data, target_file, append_data)
  try
    set the target_file to the target_file as text
    
set the open_target_file to open for access file target_file with write permission
    
if append_data is false then set eof of the open_target_file to 0
    
write this_data to the open_target_file starting at eof
    
close access the open_target_file
    
return true
  on error error_message
    try
      close access file target_file
    end try
    
return error_message
  end try
end write_to_file

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

03/18 ABS(絶対値)を求める

AppleScriptにはABS(絶対値)を求める関数は標準では装備されていません。スクリプティング機能拡張(OSAX)でABS関数を提供しているものもありますが、OSAXに頼らなくてもABS程度ならこのぐらいで求められます。

スクリプト名:ABSを求める
set x to -44
set x to absNum(x)
> 44

on absNum(q)
  if q is less than 0 then set q to -q
  
return q
end absNum

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

03/18 データのhexダンプ_v2

文字データのhexダンプを行うルーチンです。PPC MacとIntel Macではダンプ出力時のバイトオーダーが異なるので、CPUアーキテクチャを検出してそれぞれのケースに対応しています。

スクリプト名:データのhexダンプ_v2
set aData to text returned of (display dialog “input some keyword to dump” default answer “”)
set aData to1234㍿
set bData to hexDumpStr(aData) of me
bData
> “31323334337f”

set bLen to (length of bData) / 2

データの16進ダンプ(PPC/Intel対応)
高速化した
on hexDumpStr(aData)
  –バイトオーダーを取得(1234でIntel系、4321でPPC系)
  
set byteOrder to do shell scriptsysctl -n hw.byteorder
  
  –
データの書き出し準備
  
set tmpPath to path to temporary items from user domain
  
set inData to (tmpPath as string) & “.hexdumpData.txt
  
set inDataPosix to quoted form of POSIX path of inData
  
  
set aData to aData as Unicode text
  
set cList to characters of aData
  
  
set hexData to “”
  
  
write_to_file(aData, inData, false) of me
  
  
try
    set aRes to do shell scripthexdump ” & inDataPosix
  on error
    return “”
  end try
  
  
set aaList to paragraphs of aRes
  
copy items 1 thru -2 of aaList to bList
  
  
set hexText to “”
  
repeat with i in bList
    set j to contents of i
    
    
set aLine to text 9 thru -1 of j
    
set aWordsList to words of aLine
    
    
repeat with ii in aWordsList
      set jj to contents of ii
      
      
set firstByte to text 1 thru 2 of jj
      
set secondByte to text 3 thru 4 of jj
      
      
if byteOrder = “4321then
        –PPC (Big Endian)
        
if firstByte = “00then set firstByte to “”
        
set byteT to firstByte & secondByte
      else if byteOrder = “1234then
        –Intel (Little Endian )
        
if secondByte = “00then set secondByte to “”
        
set byteT to secondByte & firstByte
      end if
      
      
set hexData to hexData & byteT
      
    end repeat
  end repeat
  
  
return hexData
  
end hexDumpStr

ファイルの追記ルーチン「write_to_file」
追記データ、追記対象ファイル、boolean(trueで追記)
on write_to_file(this_data, target_file, append_data)
  try
    set the target_file to the target_file as text
    
set the open_target_file to open for access file target_file with write permission
    
if append_data is false then set eof of the open_target_file to 0
    
write this_data to the open_target_file starting at eof
    
close access the open_target_file
    
return true
  on error error_message
    try
      close access file target_file
    end try
    
return error_message
  end try
end write_to_file

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

03/15 文字列中のピリオドの個数を数える

メールアドレスの正当性をAppleScriptでチェックするときに、ドメイン名をwhoisコマンドで確認するようにしたときに、マッチしない場合にはサブドメインを削除してさかのぼっていく処理を記述。そのときに、ドメインがどの階層まで示すものか検出するためにピリオドを数える処理を入れました。

スクリプト名:文字列中のピリオドの個数を数える
set aStr to "hogehoge.co.jp"
set aRes to countPeriod(aStr) of me

on countPeriod(aStr)
  set cList to characters of aStr
  
set pCount to 0
  
repeat with i in cList
    if contents of i = "." then set pCount to pCount + 1
  end repeat
  
  
return pCount
end countPeriod

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

03/15 イベント継承実験1

本スクリプトを実行すると、logコマンドを実行しようとするのですが、logコマンドの実行を横取りしてその内容をダイアログ表示します。たまにAppleScriptの構文資料を見ると新たな発見があり、いまさらながらに驚かされるものですが……さりとて、それほど役立つとも思えず……一般的にはprintイベントハンドラやquitイベントハンドラをフックして処理を行わせるという使い方をします。

スクリプト名:イベント継承実験1
on log aParam
  display dialog aParam
end log

log "abd"

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

03/09 RSS用のRFC822形式の日付を返すv2

スクリプト名:RSS用のRFC822形式の日付を返すv2
ret_rfc822date() of me

RSS用のRFC822形式の日付を返す
on ret_rfc822date()
  set resText to do shell scriptruby -r ‘date’ -e ‘print DateTime.now.strftime(\”%a, %d %b %Y %X %Z\”)’
  
return resText
end ret_rfc822date

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

03/09 URLエンコードされたファイルパスを元のテキスト(POSIX Path)に戻す

スクリプト名:URLエンコードされたファイルパスを元のテキスト(POSIX Path)に戻す
(*
–ファイルのURLを取得
set a to choose file
tell application “Finder”
  set aURL to URL of a
end tell
*)

URLエンコードされたファイルパスをテキスト(POSIX形式)に戻す
set fileURI tofile://localhost/Users/maro/Documents/AppleEvent%E3%83%86%E3%82%99%E3%83%83%E3%83%88%E3%82%99%E3%83%AD%E3%83%83%E3%82%AF
set posixPath to (do shell scriptperl -e ‘use URI::file; print URI->new(\”” & fileURI & “\”)->file;’“)

> “/Users/maro/Documents/AppleEventデッドロック”

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

03/09 指定矩形内に含まれるデータをリストで返す

Finder上に並べたアイコンの情報をひろってきて、それらを合成して1枚ものの画像に組み立てるというAppleScriptを作ったときの、部品です。指定座標(矩形)内にファイルが存在しているかどうかという処理をこのルーチンを使って行いました。
(more…)

03/09 リストをタブ区切りのテキストに変換

スクリプト名:リストをタブ区切りのテキストに変換
set aList to {1, 2, 3, 4}
set bList to list2TABseparatedText(aList) of me

リストをタブ区切りのテキストに変換
on list2TABseparatedText(aList)
  set curDelim to AppleScript’s text item delimiters
  
set AppleScript’s text item delimiters to tab
  
set bList to aList as string
  
set AppleScript’s text item delimiters to curDelim
  
return bList
end list2TABseparatedText

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

03/09 指数表示数値を文字列化

スクリプト名:指数表示数値を文字列化
on Stringify(x) – for E+ numbers
  set x to x as string
  
set {tids, AppleScript’s text item delimiters} to {AppleScript’s text item delimiters, {"E+"}}
  
if (count (text items of x)) = 1 then
    set AppleScript’s text item delimiters to {tids}
    
return x
  else
    set {n, z} to {text item 1 of x, (text item 2 of x) as integer}
    
set AppleScript’s text item delimiters to {tids}
    
set i to character 1 of n
    
set decSepChar to character 2 of n "." or ","
    
set d to text 3 thru -1 of n
    
set l to count d
    
if l > z then
      return (i & (text 1 thru z of d) & decSepChar & (text (z + 1) thru -1 of d))
    else
      repeat (z - l) times
        set d to d & "0"
      end repeat
      
return (i & d)
    end if
  end if
end Stringify

set longNumber to "1082204521"
set exponent to longNumber as number> 1.082204521E+9
set numberString to Stringify(exponent)

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

03/09 時分秒をシステム設定と関係なく一律に取得

スクリプト名:時分秒をシステム設定と関係なく一律に取得
set {hourNum, minuteNum, secondNum} to getHHMMSS() of me

時分秒をシステム設定と関係なく一律に取得
on getHHMMSS()
  set tmp_a to current date
  
set tmp_t to time of tmp_a
  
set tmp_hourNum to ((tmp_t - tmp_t mod 3600) / 3600) as integer
  
set tmp_minuteNum to (((tmp_t mod 3600) - (tmp_t mod 3600) mod 60) / 60) as integer
  
set tmp_secondNum to (tmp_t mod 3600) mod 60
  
return {tmp_hourNum, tmp_minuteNum, tmp_secondNum}
end getHHMMSS

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

03/09 国民の祝日を求める

本ルーチンは、最新の「国民の祝日に関する法律」に対応できていないため、最新版を使用してください。また、iCalを起動してよいのであれば、「iCalに「日本の祝日」を追加」を用いて祝日カレンダーをiCalに追加して参照すると、より簡単で短いプログラムで休日情報を得られるのでよいでしょう。

スクリプト名:国民の祝日を求める
set aList to retHoliday(2015) of me

国民の祝日を求める
on retHoliday(aYear)
  set holidayList to {”1/1“, “2/11“, “4/29“, “5/3“, “5/4“, “5/5“, “11/3“, “11/23“, “12/23“}
  
set the end of holidayList to get_specifiedDay(aYear, 1, “月曜日“, 2) –成人の日
  
set the end of holidayList to get_specifiedDay(aYear, 7, “月曜日“, 3) –海の日
  
set the end of holidayList to get_specifiedDay(aYear, 9, “月曜日“, 3) –敬老の日
  
set the end of holidayList to get_specifiedDay(aYear, 10, “月曜日“, 2) –体育の日
  
set the end of holidayList to3/” & get_ShunbunNoHi(aYear) –春分の日
  
set the end of holidayList to9/” & get_ShuubunNoHi(aYear) –秋分の日
  
  
set holiDate to {}
  
repeat with i in holidayList
    set holiD to date (((aYear as text) & “/” & i) as text)
    
set holiText to holiD as text
    
    –
元日以外を対象とする
    
if (i as text) is not1/1then
      –振替休日付加処理
      
if日曜日is in holiText then
        set hurikaeD to holiD + (1 * days)
        
set the end of holiDate to hurikaeD
      end if
    end if
    
    
set the end of holiDate to holiD
    
  end repeat
  
  
return holiDate
  
end retHoliday

春分の日を求める
2000年から2099年の間まで計算可能
on get_ShunbunNoHi(aYear)
  set a to 20.69115
  
set b to (aYear - 2000) * 0.2421904
  
set c to round ((aYear - 2000) / 4) rounding toward zero
  
set d to round (a + b - c) rounding toward zero
  
return d
end get_ShunbunNoHi

秋分の日を求める
2000年から2099年の間まで計算可能
on get_ShuubunNoHi(aYear)
  set a to 23.09
  
set b to (aYear - 2000) * 0.2421904
  
set c to round ((aYear - 2000) / 4) rounding toward zero
  
set d to round (a + b - c) rounding toward zero
  
return d
end get_ShuubunNoHi

指定月の第x指定曜日に該当する日付を求める(mm/dd形式)
on get_specifiedDay(aYear, aMonth, Youbi, orderNum)
  set Youbi to Youbi as Unicode text
  
set sDat to date ((aYear & “/” & aMonth & “/1“) as text)
  
set eDat to getMlen(aYear, aMonth)
  
  
set countNum to 0
  
  
repeat with i from 1 to eDat
    set aCal to date ((aYear & “/” & aMonth & “/” & (i as text)) as text)
    
set aCalText to aCal as Unicode text
    
if Youbi is in aCalText then
      set countNum to countNum + 1
      
if countNum is orderNum then
        set aCalText to (aMonth & “/” & i as text)
        
return aCalText
      end if
    end if
  end repeat
end get_specifiedDay

指定月の曜日部分を文字列として1か月分返す
on retCalDayForMonth(paramYear, paramMonth)
  set a to date ((paramYear & “/” & paramMonth & “/1“) as text)
  
  
set dText to日 月 火 水 木 金 土 日 月 火 水 木 金 土 日 月 火 水 木 金 土 日 月 火 水 木 金 土 日 月 火 水 木 金 土 日 月 火 水 木 金 土 日 月 火 水 木 金 土 as Unicode text
  
  
set aYear to getYear(a)
  
set aMonth to getMonth(a)
  
set aDay to getDate(a)
  
  
try
    set fDay to date ((aYear & “/” & aMonth & “/1“) as text)
  on error
    return false文字列パラメータが不正であった場合にはfalseを返す
  end try
  
  
set mLen to getMlen(aYear, aMonth) –月の長さ
  
set fDate to getDay(fDay)
  
  
set tStart to fDate * 2 - 1文字列取得開始位置
  
set tEnd to tStart + (mLen * 2) - 1
  
set mText to characters tStart thru tEnd of dText
  
  
set mText to mText as Unicode text
  
return mText
end retCalDayForMonth

現在日時から曜日を取得を返す
on getDay(aDate)
  
  –
パラメータ判定部分
  
if aDate is “” then
    –ヌルの場合には本日の日付を指定
    
set aDate to current date
  else if class of aDate is not date then
    try
      –パラメータがDate型でなければ、castする
      
set aDate to date aDate
    on error number errNo
      –型変換に失敗した場合にはエラーを返す
      
error errNo
    end try
  end if
  
  
set wEList toSunday Monday Tuesday Wednesday Thursday Friday Saturday
  
  
set b to weekday of aDate
  
set c to b as text
  
  
set wiCount to offset of c in wEList
  
set wi2 to (wiCount - (wiCount mod 10) + 10) / 10
  
  
return wi2
  
end getDay

指定日の月のみ返す
on getMonth(aDat)
  –引数がDate型でなかった場合にキャスト
  
if class of aDat is not equal to date then
    try
      set aDat to date aDat
    on error
      –日付フォーマットでなかった場合には「0」を返す
      
return 0
    end try
  end if
  
set bDate to month of aDat
  
return bDate as number
end getMonth

指定日の日付のみ返す
on getDate(aDat)
  –引数がDate型でなかった場合にキャスト
  
if class of aDat is not equal to date then
    try
      set aDat to date aDat
    on error
      –日付フォーマットでなかった場合には「0」を返す
      
return 0
    end try
  end if
  
set bDate to day of aDat
  
return bDate as number
end getDate

指定日の年のみ返す
on getYear(aDat)
  –引数がDate型でなかった場合にキャスト
  
if class of aDat is not equal to date then
    try
      set aDat to date aDat
    on error
      –日付フォーマットでなかった場合には「0」を返す
      
return 0
    end try
  end if
  
set bDate to year of aDat
  
return bDate as number
end getYear

on getMlen(aYear, aMonth)
  set aYear to aYear as number
  
set aMonth to aMonth as number
  
  (*

  Parameter
    set aYear to 2004–Year
    set aMonth to 7–Month
*)
  set aDat to (aYear as text) & “/” & (aMonth as text) & “/1
  
if aMonth is 12 then
    set eDat to ((aYear + 1) as text) & “/” & (1 as text) & “/1
  else
    set eDat to ((aYear as text) & “/” & (aMonth + 1) as text) & “/1
  end if
  
  
set sDat to date aDat
  
set eDat to date eDat
  
set eDat to eDat - 1
  
  
set eDText to eDat as Unicode text
  
set eList to words of eDText
  
  –
result examle
  –
{”2004″, “年”, “7″, “月”, “31″, “日”, “土曜日”, “11″, “59″, “59″, “PM”}
  
  
set mLen to (item 5 of eList) as number
  
return mLen
end getMlen

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

03/09 指定文字を置換(リプレース)

スクリプト名:指定文字を置換(リプレース)

set a to replaceText(”.12345“, “.“, ““) of me
a

任意のデータから特定の文字列を置換
on replaceText(origData, origText, repText)
  set curDelim to AppleScript’s text item delimiters
  
set AppleScript’s text item delimiters to {origText}
  
set origData to text items of origData
  
set AppleScript’s text item delimiters to {repText}
  
set origData to origData as text
  
set AppleScript’s text item delimiters to curDelim
  –
set b to origData as text
  
return origData
end replaceText

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に