Archive for the 'ソート(sorting)' Category

2017/01/07 iTunesライブラリの曲のアーティスト名を集計

iTunesライブラリ中に入っている「曲」(song)のアーティスト名を集計するAppleScriptです。

iTunesLibraryフレームワークを用いてライブラリにアクセスしているので、iTunes.appが起動している必要はありません。

アーティスト名については、ライブラリ中にかなりイレギュラーなデータが存在しているため、対策が必要でした。

・初期のiTunesで、CDからリッピングしたものの、CDDBに登録がなかったため、アーティスト名などが登録されていない→ エラートラップで対処

・アーティスト名で姓(last name)と名(first name)の間に空白が入っていたりいなかったりするなど、表記ゆらぎが存在していたため、ゆらぎを吸収

もう少し高速に実行できてもよさそうですが、ライブラリ中のtrackが8,100程度のときに、10回実行時の平均は2.8秒程度です(MacBook Pro Retina 2012)。

AppleScript名:iTunesライブラリの曲のアーティスト名を集計
– Created 2017-01-07 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “iTunesLibrary”
–http://piyocast.com/as/archives/4379

set library to current application’s ITLibrary’s libraryWithAPIVersion:“1.0″ |error|:(missing value)
if library is equal to missing value then return

set allTracks to library’s allMediaItems()
set allCount to allTracks’s |count|()

set anEnu to allTracks’s objectEnumerator()
set newArray to current application’s NSMutableArray’s alloc()’s init()

repeat
  set aPL to anEnu’s nextObject()
  
if aPL = missing value then exit repeat
  
try
    set aKind to (aPL’s mediaKind) as integer
    
    
if (aKind as integer) is equal to 2 then –Music, Song
      set plName to aPL’s artist’s |name| as string
      
set pl2Name to (my changeThis:” “ toThat:“” inString:plName) –日本語アーティスト名で姓と名の間にスペースが入っているものがある(表記ゆらぎ)ので対策
      
newArray’s addObject:(pl2Name)
    end if
  on error
    set aLoc to (aPL’s location’s |path|()) as string
    
log aLoc
  end try
end repeat

set aRes to countItemsByItsAppearance(newArray) of me
–>  {{theName:”浜田省吾”, numberOfTimes:442}, {theName:”B’z”, numberOfTimes:379}, {theName:”渡辺岳夫・松山祐士”, numberOfTimes:199}, {theName:”VariousArtists”, numberOfTimes:192}, {theName:”菅野よう子”, numberOfTimes:108}, {theName:”布袋寅泰”, numberOfTimes:100}, {theName:”三枝成彰”, numberOfTimes:95}, {theName:”宇多田ヒカル”, numberOfTimes:94}, {theName:”宮川泰”, numberOfTimes:81}, {theName:”MichaelJackson”, numberOfTimes:78}, {theName:”稲葉浩志”, numberOfTimes:73}, …

–出現回数で集計
on countItemsByItsAppearance(aList)
  set aSet to current application’s NSCountedSet’s alloc()’s initWithArray:aList
  
set bArray to current application’s NSMutableArray’s array()
  
set theEnumerator to aSet’s objectEnumerator()
  
  
repeat
    set aValue to theEnumerator’s nextObject()
    
if aValue is missing value then exit repeat
    
bArray’s addObject:(current application’s NSDictionary’s dictionaryWithObjects:{aValue, (aSet’s countForObject:aValue)} forKeys:{“theName”, “numberOfTimes”})
  end repeat
  
  
–出現回数(numberOfTimes)で降順ソート
  
set theDesc to current application’s NSSortDescriptor’s sortDescriptorWithKey:“numberOfTimes” ascending:false
  
bArray’s sortUsingDescriptors:{theDesc}
  
  
return bArray as list
end countItemsByItsAppearance

on changeThis:findString toThat:repString inString:someText
  set theString to current application’s NSString’s stringWithString:someText
  
set theString to theString’s stringByReplacingOccurrencesOfString:findString withString:repString options:(current application’s NSRegularExpressionSearch) range:{location:0, |length|:length of someText}
  
return theString as text
end changeThis:toThat:inString:

★Click Here to Open This Script 

2016/12/14 レコードのうち、最大の値を持つKeyを返す

レコードのうち、最大の値を持つキーを返すAppleScriptです。

AppleScriptからMicrosoftの表情認識APIを呼び出せるようになったはいいものの、得られたデータからどの表情の可能性が高いかを判定する必要があり、そのために書いてみたものです。

Cocoaの機能を用いて、レコードのキーを取り出したり、さまざまな処理を行っています。こうした処理が手軽にできるようになったのは、実にいいことです。

さらに、画像から顔認識を行い、表情を読み取ってデータ化できるというのは、実に素晴らしいと思うものです。

ただ、いろいろ実験を行ってみたところ・・・恐れとか悲しみとか軽蔑といった微妙な感情については検出しづらいことがわかってきました。最大の値のものをピックアップするという方法ではなく、微妙な感情については別途評価を行う必要がありそうです。

mona_lisa_by_leonardo_da_vinci_from_c2rmf_retouched-1.jpg
–> “neutral”

donaldtrump61815_resized.png
–> “anger”

AppleScript名:レコードのうち、最大の値を持つKeyを返す
– Created 2016-12-14 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4359

set aDict to current application’s NSDictionary’s dictionaryWithDictionary:{sadness:0.01133416, anger:1.03975857E-4, happiness:2.90919736E-4, fear:2.28432211E-4, neutral:0.9830475, contempt:2.4698046E-4, disgust:1.02946949E-4, surprise:0.00464509334}
set aMacKey to retMaxValueKey(aDict) of me
–>  ”neutral”

on retMaxValueKey(aDict)
  set aValList to aDict’s allValues()
  
set maxVal to (sort1DNumList(aValList, false) of me)’s firstObject()
  
set keyList to (aDict’s allKeys()) as list
  
  
repeat with i in keyList
    set j to contents of i
    
set aTmp to (aDict’s valueForKey:j)
    
set aRes to compareNumerically(maxVal, aTmp) of me
    
if aRes = true then return j
  end repeat
  
  
return false
end retMaxValueKey

–Numerical Strings Compare
on compareNumerically(aText as text, bText as text)
  set aStr to current application’s NSString’s stringWithString:aText
  
return (aStr’s compare:bText options:(current application’s NSNumericSearch)) = current application’s NSOrderedSame
end compareNumerically

–1D List(数値)をsort / ascOrderがtrueだと昇順ソート、falseだと降順ソート
on sort1DNumList(theList, aBool)
  set theSet to current application’s NSSet’s setWithArray:theList
  
set theDescriptor to current application’s NSSortDescriptor’s sortDescriptorWithKey:(missing value) ascending:aBool
  
set sortedList to theSet’s sortedArrayUsingDescriptors:{theDescriptor}
  
return (sortedList)
end sort1DNumList

★Click Here to Open This Script 

2016/09/20 連番JPEGファイルを読み込んで連結したPDFを作成(新規作成)

連番JPEG画像を番号順にソートして、順次連結したPDFを新規作成するAppleScriptです。

jpeg_catfiles.jpg

このような連番画像を連結して任意のファイル名のPDFに合成します。出来上がるPDFは、元のJPEGファイルの圧縮度に応じて大きくなります。

新規作成よりも、すでに存在しているPDFにJPEGファイルを連結するほうが実用的だと思います。

AppleScript名:連番JPEGファイルを読み込んで連結したPDFを作成(新規作成)
– Created 2016-09-20 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “QuartzCore”
use framework “Quartz”
use framework “AppKit”

set aExt to “.jpg”
set aFol to choose folder
set fList to getFilePathList(aFol, aExt) of me
set f2List to my sort1DList:fList ascOrder:true –sort by ascending

set newFile to POSIX path of (choose file name with prompt “新規PDFファイルの名称を選択”)
set newFilePath to current application’s NSString’s stringWithString:newFile

–Make Blank PDF
set aPDFdoc to current application’s PDFDocument’s alloc()’s init()

set pageNum to 0

repeat with i in f2List
  set j to contents of i
  
set aURL to (current application’s |NSURL|’s fileURLWithPath:j)
  
set bImg to (current application’s NSImage’s alloc()’s initWithContentsOfURL:aURL)
  (
aPDFdoc’s insertPage:(current application’s PDFPage’s alloc()’s initWithImage:bImg) atIndex:pageNum)
  
set pageNum to pageNum + 1
end repeat

aPDFdoc’s writeToFile:newFilePath

–ASOCで指定フォルダのファイルパス一覧取得(拡張子指定つき)
on getFilePathList(aFol, aExt)
  set aPath to current application’s NSString’s stringWithString:(POSIX path of aFol)
  
set aFM to current application’s NSFileManager’s defaultManager()
  
set nameList to (aFM’s contentsOfDirectoryAtPath:aPath |error|:(missing value)) as list
  
set anArray to current application’s NSMutableArray’s alloc()’s init()
  
  
repeat with i in nameList
    set j to i as text
    
if (j ends with aExt) and (j does not start with “.”) then –exclude invisible files
      set newPath to (aPath’s stringByAppendingString:j)
      (
anArray’s addObject:newPath)
    end if
  end repeat
  
  
return anArray as list
end getFilePathList

–1D List(文字)をsort / ascOrderがtrueだと昇順ソート、falseだと降順ソート
on sort1DList:theList ascOrder:aBool
  set aDdesc to current application’s NSSortDescriptor’s sortDescriptorWithKey:“self” ascending:aBool selector:“localizedCaseInsensitiveCompare:”
  
set theArray to current application’s NSArray’s arrayWithArray:theList
  
return (theArray’s sortedArrayUsingDescriptors:{aDdesc}) as list
end sort1DList:ascOrder:

★Click Here to Open This Script 

2016/07/28 Bookフォルダリネーム

書籍の原稿管理用のAppleScriptです。原稿は章ごとにフォルダ分けしており、

 9999 フォルダ名あるいはファイル名

といったように、仮想ノンブルを前に付けています。この仮想ノンブルで前後関係を制御しており、基本的に数字の大小だけが重要です。

whole.png

最後に1冊分PDFにまとめてビルドするAppleScriptで、書き出し対象のフォルダ以下のMarkdownファイルとPagesのファイルをピックアップし、ファイル名でソート。ソート順は仮想ノンブルによってコントロールされることになります。

Finderの並べ替えを使っているので、シンプルでわかりやすく、使い勝手もよいのですが……ただひとつ難点が。

途中にコンテンツを挿入したい場合にも仮想ノンブルでコントロールしつつ、途中に入るように操作するのですが……場合によってはフォルダおよび書類の仮想ノンブル部分だけリネームする必要が出てきます。

とくに、目下改定作業中の「AppleScript最新リファレンス」のコマンドリファレンス部分は、初版から大幅に改定・補充を行っているため、ひんぱんにリネームが発生して大変です(ーー;;

そこで、こんなScriptを組んでリネーム作業だけ自動化してみました。

Finder上でフォルダを選択して本Scriptを実行すると、選択中のフォルダ名から仮想ノンブルの番号を取得し、

before2.png

内包するフォルダに入っているファイルについても、すべて仮想ノンブル部分を書き換えます。

after2.png

AppleScript名:Bookフォルダリネーム
– Created 2016-07-28 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use framework “Foundation”
use scripting additions
# http://piyocast.com/as/archives/4177

set aStep to 10

tell application “Finder”
  set aSel to (selection as alias list)
end tell
if aSel = {} or aSel = “” then return
set aFol to first item of aSel

tell application “Finder”
  –選択中のフォルダの名称を取得
  
set aName to name of aFol
  
  
–選択中のフォルダに入っている書類を取得
  
tell folder (aFol as string)
    set fList to name of every file as alias list
  end tell
end tell

set {startNumStr, folderNameStr} to parseNumAndString(aName) of me
set startNum to startNumStr as integer

set aRes to sort1DListOrder(fList, true) of me –昇順でファイル名をソート

set aFolStr to aFol as string
set aCount to startNumStr
tell application “Finder”
  repeat with i in aRes
    set anAlias to (aFolStr & (i as string)) as alias
    
set tmpName to name of anAlias
    
    
set {curNum, curName} to parseNumAndString(tmpName) of me
    
set newName to (retZeroPaddingText(aCount, length of curNum) of me) & ” “ & curName
    
    
set name of anAlias to newName
    
    
set aCount to aCount + aStep
  end repeat
end tell

–文字列から先頭にあるとおぼしき数値と、スペースで区切られた後続の文字列を分離する
on parseNumAndString(aName)
  set aOffset to offset of ” “ in aName
  
if aOffset = 0 then error “Illigal Format”
  
set aNum to text 1 thru (aOffset - 1) of aName
  
set aText to text (aOffset + 1) thru -1 of aName
  
return {aNum, aText}
end parseNumAndString

–1D Listをsort / ascOrderがtrueだと昇順ソート、falseだと降順ソート
on sort1DListOrder(theList, aBool)
  set aDdesc to current application’s NSSortDescriptor’s sortDescriptorWithKey:“self” ascending:aBool selector:“localizedCaseInsensitiveCompare:”
  
set theArray to current application’s NSArray’s arrayWithArray:theList
  
return (theArray’s sortedArrayUsingDescriptors:{aDdesc}) as list
end sort1DListOrder

on retZeroPaddingText(aNum as integer, aDigitNum as integer)
  if aNum > (((10 ^ aDigitNum) as integer) - 1) then return “” –Range Check
  
set aFormatter to current application’s NSNumberFormatter’s alloc()’s init()
  
aFormatter’s setUsesGroupingSeparator:false
  
aFormatter’s setAllowsFloats:false
  
aFormatter’s setMaximumIntegerDigits:aDigitNum
  
aFormatter’s setMinimumIntegerDigits:aDigitNum
  
aFormatter’s setPaddingCharacter:“0″
  
set aStr to aFormatter’s stringFromNumber:(current application’s NSNumber’s numberWithFloat:aNum)
  
return aStr as string
end retZeroPaddingText

★Click Here to Open This Script 

2016/06/18 指定フォルダ以下にあるMacDownとPages書類をソートしてPDFに書き出す

指定フォルダ以下にあるMacDownで記述したMarkdown書類と、Pages書類をすべての階層からピックアップしてファイル名でソートして、すべてデスクトップにPDFで書き出すAppleScriptです。

コンパイル(構文確認)および実行に際しては、Shane StanleyのScript Library「Bridge Plus」をインストールしておく必要があります。また、GUI Scriptingを利用しているため「システム環境設定」>「セキュリティとプライバシー」>「プライバシー」>「アクセシビリティ」でスクリプトエディタ(アプレットとして実行する場合にはアプレットそのもの)を登録して許可しておく必要があります。

「技術書典」に出す電子ブックのフォーマットがギリギリまで決まらず、しかも縦長のスクロールさせるタイプのものにできないかとあがいていたのですが、結局iPadあたりで読むことを考慮するとiPadのリーダーの仕様にしたがう必要があります。

……あれ?(^ー^; 結局、ページめくりは発生するし、一般的な本と同じような体裁になってしまいますよ → フォーマットがPDFになりました。

Markdown書類とPages書類が混在しているフォルダ構造のトップ階層のフォルダを指定すると、Markdown書類とPages書類をピックアップし、ファイル名でそれらをソートし、順次PDFに書き出すAppleScriptを書いてみました(必要は発明のマザー!)。

book1.png

ただ、MacDownには「書類をPDFに書き出す」という機能がAppleScript側に公開されていません(T_T)。

macdown_dict.png

ないものを「ないない」と嘆いても仕方がないので、さっさとGUI Scriptingで強制的にメニュー操作することにしました。

macdown_gui.png

で、どこに? どこに保存させるのでしょう??

大丈夫! そんなときには、保存ダイアログで幾つかのフォルダに強制的に移動させるキーボードショートカットが存在しており、Command-Dは「カレントディレクトリをデスクトップに移動させる」=「保存先をデスクトップにする」働きをします。

このため、保存先を操作しづらい(不可能とはいいませんけれども)GUI Scriptingにおいて保存先を指定することが、デスクトップフォルダについては可能になっています。

Pagesの方はひじょうに素直に(GUI Scriptingなんて使わずに)PDF書き出しが可能です。

そんなわけで、時間に追い詰められながらもなんとか大量のデータ処理を行っているのでありました。書き出した大量のPDFもAppleScriptでさくっと連結できるので、非常にいい感じです。あとは、本が完成すれば、、、、

AppleScript名:指定フォルダ以下にあるMDとPagesをソートしてPDFに書き出す
– Created 2016-06-17 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use BridgePlus : script “BridgePlus”
–use spotLight : script “spotlightLib”

property searchRes : {}

load framework

set origPath to POSIX path of (choose folder with prompt “Markdown/Pages ファイルの入っているフォルダを選択”)

set aResList to (spotlightSearch(origPath, “kMDItemKind == ’Markdown’ || kMDItemKind == ’Pages 一般書類’”) of me) as list –Caution! this parameter is *localized*

–フルパスとファイル名のペアの2D Listを作成
set newList to {}
repeat with i in aResList
  set j to contents of i
  
set aStr to (current application’s NSString’s stringWithString:j)
  
set aFileName to aStr’s lastPathComponent()
  
set the end of newList to {aStr, aFileName}
end repeat

–番号順にソート
set sortIndexes to {1} –Key Item id: begin from 0, Sort by filename
set sortOrders to {true}
set sortTypes to {“compare:”}
set resList to (current application’s SMSForder’s subarraysIn:(newList) sortedByIndexes:sortIndexes ascending:sortOrders sortTypes:sortTypes |error|:(missing value)) as list
if resList = {} then return –No Result

–ソートした順番にMarkdownファイル/Pages書類ファイルをオープンしてデスクトップにPDF生成してクローズ
repeat with i in newList
  copy i to {fullPath, aFileName}
  
  
set apFile to (POSIX file (fullPath as string))
  
set anAlias to apFile as alias
  
set aFileName to aFileName as string
  
  
if aFileName ends with “.md” then
    exportFromMacDown(anAlias) of me –Markdown
  else if aFileName ends with “.pages” then
    exportFromPages(anAlias) of me –Pages
  end if
end repeat

–指定のPagesファイル(alias)をデスクトップ上にPDFで書き出し
on exportFromPages(anAlias)
  tell application “Finder”
    set aName to name of anAlias
  end tell
  
  
set dtPath to (path to desktop) as string
  
set outPath to dtPath & aName & “.pdf”
  
  
tell application “Pages”
    close every document without saving
    
open anAlias
    
export document 1 to file outPath as PDF with properties {image quality:Best}
    
close every document without saving
  end tell
end exportFromPages

–指定のMacDownファイル(alias)をデスクトップ上にPDFで書き出し
on exportFromMacDown(anAlias)
  tell application “MacDown”
    open {anAlias}
  end tell
  
  
tell current application
    delay 1 –ここの時間待ちが少ないと画像抜けが発生?
  end tell
  
macDownForceSave() of me
  
  
tell application “MacDown”
    close every document without saving
  end tell
end exportFromMacDown

–注意!! ここでGUI Scriptingを使用。バージョンが変わったときにメニュー階層などの変更があったら書き換え
on macDownForceSave()
  activate application “MacDown”
  
tell application “System Events”
    tell process “MacDown”
      – File > Export > PDF
      
click menu item 2 of menu 1 of menu item 14 of menu 1 of menu bar item 3 of menu bar 1
      
      
–Go to Desktop Folder
      
keystroke “d” using {command down}
      
      
–Save Button on Sheet
      
click button 1 of sheet 1 of window 1
    end tell
  end tell
end macDownForceSave

–Spotlight Libの内容を引っ張り出してきた
on spotlightSearch(origPOSIXpath, aCondition)
  
  
set searchRes to {} –initialize
  
  
initiateSearchForFullPath(aCondition, origPOSIXpath) –Predicate & Scope Directory
  
  
–Waiting for the result
  
repeat while searchRes = {}
    current application’s NSThread’s sleepForTimeInterval:(“0.001″ as real) –delay 0.001
  end repeat
  
  
set anObj to searchRes’s firstObject() –Pick up the first one for test
  
if anObj = missing value then return {} –No Result
  
  
–set anAttrList to anObj’s attributes() –”mdls” attributes
  
–>  (NSArray) {”kMDItemContentTypeTree”, “kMDItemContentType”, “kMDItemPhysicalSize”, …}
  
  
set resArray to {}
  
repeat with anItem in my searchRes
    set j to contents of anItem
    
set aPath to (j’s valueForAttribute:“kMDItemPath”) as string
    
set the end of resArray to aPath
  end repeat
  
  
return resArray
end spotlightSearch

on initiateSearchForFullPath(aQueryStrings, origPath)
  
  
set aSearch to current application’s NSMetadataQuery’s alloc()’s init()
  
  
current application’s NSNotificationCenter’s defaultCenter()’s addObserver:(me) selector:“queryDidUpdate:” |name|:(current application’s NSMetadataQueryDidUpdateNotification) object:aSearch
  
current application’s NSNotificationCenter’s defaultCenter()’s addObserver:(me) selector:“initalGatherComplete:” |name|:(current application’s NSMetadataQueryDidFinishGatheringNotification) object:aSearch
  
  
set aPredicate to current application’s NSPredicate’s predicateWithFormat:aQueryStrings
  
aSearch’s setPredicate:aPredicate
  
  
set aScope to current application’s NSArray’s arrayWithObjects:{origPath}
  
aSearch’s setSearchScopes:aScope
  
  
set sortKeys to current application’s NSSortDescriptor’s sortDescriptorWithKey:“kMDItemFSName” ascending:true
  
aSearch’s setSortDescriptors:(current application’s NSArray’s arrayWithObject:sortKeys)
  
  
aSearch’s startQuery()
  
end initiateSearchForFullPath

on queryDidUpdate:sender
  –  
end queryDidUpdate:

on initalGatherComplete:sender
  set anObject to sender’s object
  
anObject’s stopQuery()
  
current application’s NSNotificationCenter’s defaultCenter()’s removeObserver:me |name|:(current application’s NSMetadataQueryDidUpdateNotification) object:anObject
  
current application’s NSNotificationCenter’s defaultCenter()’s removeObserver:me |name|:(current application’s NSMetadataQueryDidFinishGatheringNotification) object:anObject
  
set my searchRes to anObject’s results()
end initalGatherComplete:

★Click Here to Open This Script 

2016/02/28 Safariの履歴を読んでトップ10をテキストで出力する

SafariのHistory.plistを読み込んで、アクセス回数のトップ10をテキスト出力するAppleScriptです。

Safari 9.1+OS X 10.11環境を前提としています。plistの内容については、Safariのバージョンごとに変更が加わってもおかしくないので、メジャーバージョンアップ時には動作確認する必要があることでしょう。

他のアプリケーションの.plistファイルにアクセスするのはかなり野蛮な処理ですが(Sandbox環境では許可されない)、場合によっては有用なこともあるでしょう。

ちなみに、Scriptの実行例はサブマシン(MacBook Air 2011)で実行したものです。

AppleScript名:Safariの履歴を読んでトップ10をテキストで出力する
– Created 2016-02-27 by Takaaki Naganoya
– Piyomaru Software 2016
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

set libPath to (POSIX path of (path to library folder from user domain)) & “Safari/History.plist”
set aRec to retDictFromPlist(libPath) of me
set bRecList to aRec’s valueForKeyPath:“WebHistoryDates”

set bArray to sortRecListByLabel(bRecList, “visitCount”, false) of me
set outStr to “”
set aCount to 0

repeat with i from 0 to 100
  set anItem to (bArray’s objectAtIndex:i)
  
  
set aURL to (anItem’s valueForKeyPath:“”) as string
  
set aTitle to (anItem’s valueForKeyPath:“title”) as string
  
set aDispTItle to (anItem’s valueForKeyPath:“displayTitle”) as string
  
set aVisitCount to (anItem’s valueForKeyPath:“visitCount”) as string
  
set redirectURLs to (anItem’s valueForKeyPath:“redirectURLs”) as list
  
set lastVisitedDate to (anItem’s valueForKeyPath:“lastVisitedDate”) as string
  
  
if aURL does not start with “http://piyocast.com” then –自分のところのサイト関連を除外
    set aStr to ((aCount + 1) as string) & “:(” & (aVisitCount as string) & “)” & aTitle & return
    
set outStr to outStr & aStr
    
    
set aCount to aCount + 1
    
if aCount = 10 then exit repeat
    
  end if
  
  
end repeat

return outStr

(*
–>  ”1:(7657)radiko.jp
2:(608)Apple
3:(416)ソフトバンクWi-Fiスポット
4:(156)公式Apple Store(日本)- iPad Air、MacBook Pro、ほかにもたくさん
5:(125)Yahoo! JAPAN
6:(108)Mac整備済製品 - Apple Store (Japan)
7:(54)Apple-Style \”アップルなBlogをリンク\”
8:(44)PC Watch
9:(31)Facebook
10:(28)アップル - Mac Pro

*)

–Read plist
on retDictFromPlist(aPath as text)
  set thePath to current application’s NSString’s stringWithString:aPath
  
set thePath to thePath’s stringByExpandingTildeInPath()
  
set theDict to current application’s NSDictionary’s dictionaryWithContentsOfFile:thePath
  
return theDict
end retDictFromPlist

–リストに入れたレコードを、指定の属性ラベルの値でソート
on sortRecListByLabel(aRecList, aLabelStr, ascendF)
  set aArray to current application’s NSArray’s arrayWithArray:aRecList
  
set sortDesc to current application’s NSSortDescriptor’s alloc()’s initWithKey:aLabelStr ascending:ascendF
  
set sortDescArray to current application’s NSArray’s arrayWithObjects:sortDesc
  
set sortedArray to aArray’s sortedArrayUsingDescriptors:sortDescArray
  
return sortedArray
end sortRecListByLabel

★Click Here to Open This Script 

2014/12/25 アプリケーションのローカライズ分布を取得する v5

/Applicationsフォルダ内に入っているアプリケーションのローカライズ言語の度数分布を取得するAppleScriptのバージョンアップ版です。

1つのアプリケーションが複数の重複するローカライズ情報を持っているケース({”de”, “English”, “fr”, “French”, “German”, “ja”, “Japanese”})があり、1つのアプリケーションから情報を取得する段階で重複を除去するルーチンを作成。重複を排除して({”German”, “English”, “French”,”Japanese”})のように整理して取得するように変更しました。

また、/Applicationsフォルダの直下のアプリケーションのファイルしか取得していなかったので、mdfindで/Applicationsフォルダ以下すべてのアプリケーションを取得するように変更。

また、言語名ではなく出現頻度でソートするように変更しました。

途中で、mdfindでアプリケーションのファイルを抽出する処理を試していたら、サブフォルダ内のアプリケーションが出てこないという問題があり・・・開発マシンのSSD内のspotlightの検索辞書が壊れていたようで、再生成したら正常に動作するようになりました。mdfindの処理の落とし穴です。

マイナー言語(失礼!)をこまかくフォローしているのはGoogleアプリかと思っていたら、VLCでした。OSがサポートしていない言語用のローカライズを行っても意味はないのですが、ムービーの字幕表示などの関係でしょうか?

AppleScript名:アプリケーションのローカライズ分布を取得する v5
– Created 2014-12-23 by Takaaki Naganoya
– Changed 2014-12-24 by Shane Stanley
– Changed 2014-12-25 by Takaaki Naganoya

use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “ASObjCExtras”

–mdfindで指定フォルダ以下にあるすべてのアプリケーションファイルを取得
set apPath1 to (path to applications folder) as string
set apList to getFileListWithSpotLight(“kMDItemContentType”, “com.apple.application-bundle”, apPath1) of me

set theCountedSet to current application’s NSCountedSet’s |set|()

–各アプリケーションのローカリゼーション情報を取得する
repeat with i in apList
  set j to contents of i
  
  
–指定アプリケーションのローカライズ言語を取得。重複を除去
  
set locList to getSpecifiedAppFilesLocalizationList(j) of me
  
–> {”de”, “English”, “fr”, “French”, “German”, “ja”, “Japanese”}
  
—-> {”German”, “English”, “French”,”Japanese”}
  
  
set locArray to (current application’s SMSFord’s Cocoaify:locList)
  
  
set theEnumerator to locArray’s objectEnumerator()
  
repeat
    set aValue to theEnumerator’s nextObject()
    
if aValue is missing value then exit repeat
    
    (
theCountedSet’s addObject:aValue)
  end repeat
end repeat

–NSCountedSetをNSMutableArrayに変換
set theArray to current application’s NSMutableArray’s array()
set theEnumerator to theCountedSet’s objectEnumerator()

–NSMutableArrayから順次取り出して、NSArrayに対してDictionaryを追加
repeat
  set aValue to theEnumerator’s nextObject()
  
if aValue is missing value then exit repeat
  
theArray’s addObject:(current application’s NSDictionary’s dictionaryWithObjects:{aValue, (theCountedSet’s countForObject:aValue)} forKeys:{“theName”, “numberOfTimes”})
end repeat

–出現回数(numberOfTimes)で降順ソート
set theDesc to current application’s NSSortDescriptor’s sortDescriptorWithKey:“numberOfTimes” ascending:false
theArray’s sortUsingDescriptors:{theDesc}

return theArray as list

–> {{theName:”English”, numberOfTimes:733}, {theName:”Japanese”, numberOfTimes:404}, {theName:”French”, numberOfTimes:266}, {theName:”German”, numberOfTimes:265}, {theName:”Italian”, numberOfTimes:219}, {theName:”Spanish”, numberOfTimes:203}, {theName:”Dutch”, numberOfTimes:165}, {theName:”Chinese (China)”, numberOfTimes:149}, {theName:”Swedish”, numberOfTimes:133}, {theName:”Korean”, numberOfTimes:131}, {theName:”Russian”, numberOfTimes:129}, {theName:”Chinese (Taiwan)”, numberOfTimes:127}, {theName:”Portuguese”, numberOfTimes:124}, {theName:”Polish”, numberOfTimes:118}, {theName:”Danish”, numberOfTimes:117}, {theName:”Finnish”, numberOfTimes:110}, {theName:”Norwegian”, numberOfTimes:100}, {theName:”Czech”, numberOfTimes:92}, {theName:”Turkish”, numberOfTimes:90}, {theName:”Hungarian”, numberOfTimes:87}, {theName:”Greek”, numberOfTimes:76}, {theName:”Ukrainian”, numberOfTimes:75}, {theName:”Romanian”, numberOfTimes:74}, {theName:”Portuguese (Portugal)”, numberOfTimes:70}, {theName:”Slovak”, numberOfTimes:69}, {theName:”Catalan”, numberOfTimes:63}, {theName:”Croatian”, numberOfTimes:63}, {theName:”Arabic”, numberOfTimes:61}, {theName:”Thai”, numberOfTimes:61}, {theName:”Hebrew”, numberOfTimes:60}, {theName:”Indonesian”, numberOfTimes:58}, {theName:”Vietnamese”, numberOfTimes:57}, {theName:”Malay”, numberOfTimes:56}, {theName:”Spanish (Mexico)”, numberOfTimes:49}, {theName:”Base”, numberOfTimes:46}, {theName:”Portuguese (Brazil)”, numberOfTimes:22}, {theName:”Norwegian Bokmål”, numberOfTimes:21}, {theName:”Chinese (Simplified)”, numberOfTimes:19}, {theName:”Chinese”, numberOfTimes:14}, {theName:”English (United Kingdom)”, numberOfTimes:12}, {theName:”Bulgarian”, numberOfTimes:12}, {theName:”Chinese (Traditional)”, numberOfTimes:11}, {theName:”Latvian”, numberOfTimes:10}, {theName:”Lithuanian”, numberOfTimes:9}, {theName:”Slovenian”, numberOfTimes:9}, {theName:”Japanese (Japan)”, numberOfTimes:9}, {theName:”Czech (Czech Republic)”, numberOfTimes:9}, {theName:”Estonian”, numberOfTimes:8}, {theName:”Ukrainian (Ukraine)”, numberOfTimes:8}, {theName:”Spanish (Spain)”, numberOfTimes:7}, {theName:”Turkish (Turkey)”, numberOfTimes:7}, {theName:”Hungarian (Hungary)”, numberOfTimes:6}, {theName:”Danish (Denmark)”, numberOfTimes:6}, {theName:”Russian (Russia)”, numberOfTimes:6}, {theName:”French (XM)”, numberOfTimes:6}, {theName:”Swedish (Sweden)”, numberOfTimes:6}, {theName:”Serbian”, numberOfTimes:6}, {theName:”Norwegian Bokmål (Norway)”, numberOfTimes:6}, {theName:”Dutch (Netherlands)”, numberOfTimes:6}, {theName:”Persian”, numberOfTimes:5}, {theName:”Korean (South Korea)”, numberOfTimes:5}, {theName:”Finnish (Finland)”, numberOfTimes:5}, {theName:”Italian (Italy)”, numberOfTimes:5}, {theName:”German (Germany)”, numberOfTimes:5}, {theName:”French (Canada)”, numberOfTimes:5}, {theName:”French (France)”, numberOfTimes:5}, {theName:”Polish (Poland)”, numberOfTimes:5}, {theName:”Hindi”, numberOfTimes:5}, {theName:”English (United States)”, numberOfTimes:5}, {theName:”Hebrew (Israel)”, numberOfTimes:4}, {theName:”Arabic (United Arab Emirates)”, numberOfTimes:4}, {theName:”Spanish (Latin America)”, numberOfTimes:4}, {theName:”Romanian (Romania)”, numberOfTimes:4}, {theName:”Spanish (Namibia)”, numberOfTimes:4}, {theName:”Filipino”, numberOfTimes:4}, {theName:”Spanish (Laos)”, numberOfTimes:4}, {theName:”empty”, numberOfTimes:4}, {theName:”Greek (Greece)”, numberOfTimes:4}, {theName:”Telugu”, numberOfTimes:3}, {theName:”Malayalam”, numberOfTimes:3}, {theName:”Icelandic”, numberOfTimes:3}, {theName:”Kannada”, numberOfTimes:3}, {theName:”Galician”, numberOfTimes:3}, {theName:”Bengali”, numberOfTimes:3}, {theName:”Gujarati”, numberOfTimes:3}, {theName:”Marathi”, numberOfTimes:3}, {theName:”Tamil”, numberOfTimes:3}, {theName:”Belarusian”, numberOfTimes:3}, {theName:”Albanian”, numberOfTimes:2}, {theName:”Mongolian”, numberOfTimes:2}, {theName:”French (Morocco)”, numberOfTimes:2}, {theName:”Georgian”, numberOfTimes:2}, {theName:”Sinhala”, numberOfTimes:2}, {theName:”jp”, numberOfTimes:2}, {theName:”ua”, numberOfTimes:2}, {theName:”Amharic”, numberOfTimes:2}, {theName:”English (United Arab Emirates)”, numberOfTimes:2}, {theName:”Finnish (FL)”, numberOfTimes:2}, {theName:”Catalan (Spain)”, numberOfTimes:2}, {theName:”Basque”, numberOfTimes:2}, {theName:”English (Israel)”, numberOfTimes:2}, {theName:”Norwegian (NB)”, numberOfTimes:2}, {theName:”Oriya”, numberOfTimes:1}, {theName:”Punjabi”, numberOfTimes:1}, {theName:”Serbo-Croatian”, numberOfTimes:1}, {theName:”Walloon”, numberOfTimes:1}, {theName:”Corsican”, numberOfTimes:1}, {theName:”Sorani Kurdish”, numberOfTimes:1}, {theName:”Chinese (Hong Kong SAR China)”, numberOfTimes:1}, {theName:”Breton”, numberOfTimes:1}, {theName:”Cornish”, numberOfTimes:1}, {theName:”Chiga”, numberOfTimes:1}, {theName:”Aragonese”, numberOfTimes:1}, {theName:”Macedonian”, numberOfTimes:1}, {theName:”Japan”, numberOfTimes:1}, {theName:”Bosnian”, numberOfTimes:1}, {theName:”Ganda”, numberOfTimes:1}, {theName:”Tetum”, numberOfTimes:1}, {theName:”Welsh”, numberOfTimes:1}, {theName:”Armenian”, numberOfTimes:1}, {theName:”Kyrgyz”, numberOfTimes:1}, {theName:”Scottish Gaelic”, numberOfTimes:1}, {theName:”Afrikaans”, numberOfTimes:1}, {theName:”Zulu”, numberOfTimes:1}, {theName:”Swahili”, numberOfTimes:1}, {theName:”Burmese”, numberOfTimes:1}, {theName:”Chinese (Traditional, Taiwan)”, numberOfTimes:1}, {theName:”ct”, numberOfTimes:1}, {theName:”Tagalog”, numberOfTimes:1}, {theName:”Kazakh”, numberOfTimes:1}, {theName:”Bengali (India)”, numberOfTimes:1}, {theName:”Pashto”, numberOfTimes:1}, {theName:”Nepali”, numberOfTimes:1}, {theName:”Acoli”, numberOfTimes:1}, {theName:”Friulian”, numberOfTimes:1}, {theName:”Norwegian Nynorsk”, numberOfTimes:1}, {theName:”Asturian”, numberOfTimes:1}, {theName:”Thai (Thailand)”, numberOfTimes:1}, {theName:”Khmer”, numberOfTimes:1}, {theName:”Irish”, numberOfTimes:1}, {theName:”American”, numberOfTimes:1}, {theName:”Fulah”, numberOfTimes:1}, {theName:”Chinese (Traditional, Hong Kong SAR China)”, numberOfTimes:1}, {theName:”Occitan”, numberOfTimes:1}, {theName:”Interlingua”, numberOfTimes:1}, {theName:”Uzbek”, numberOfTimes:1}, {theName:”Azerbaijani”, numberOfTimes:1}}

–指定アプリケーションファイルの、指定Localeにおけるローカライズ言語リストを求める。重複を排除
on getSpecifiedAppFilesLocalizationList(anAppAlias as alias)
  
  
set aURL to (current application’s SMSFord’s URLFrom:anAppAlias)
  
set aBundle to current application’s NSBundle’s bundleWithURL:aURL
  
  
set theNSLocale to current application’s NSLocale’s localeWithLocaleIdentifier:“en” –Output Locale Name in English (en)
  
  
–Get Localization Info
  
set locList to aBundle’s localizations()
  
–> {”de”, “English”, “fr”, “French”, “German”, “ja”, “Japanese”}
  
set theEnumerator to locList’s objectEnumerator()
  
  
set theSet to current application’s NSMutableSet’s |set|()
  
  
repeat
    set aValue to theEnumerator’s nextObject()
    
if aValue is missing value then exit repeat
    
    
set theName to (theNSLocale’s displayNameForKey:(current application’s NSLocaleIdentifier) value:aValue)
    
    
if theName is missing value then
      –”English”や”French”などは、missing valueが返ってくるので、”English”や”French”などをそのまま追加
      
theSet’s addObject:aValue
    else
      –”de”や”fr”などは、”German”や”French”が返ってくるので、それを追加
      
theSet’s addObject:theName
    end if
  end repeat
  
  
–NSCountedSetをNSMutableArrayに変換
  
set theArray to current application’s NSMutableArray’s array()
  
set theEnumerator to theSet’s objectEnumerator()
  
  
–NSMutableArrayから順次取り出して、NSArrayに対してDictionaryを追加
  
repeat
    set aValue to theEnumerator’s nextObject()
    
if aValue is missing value then exit repeat
    
theArray’s addObject:aValue
  end repeat
  
  
return theArray as list
  
end getSpecifiedAppFilesLocalizationList

–指定階層下で、指定メタデータが指定パラメータであるファイルを取得(alias list)
on getFileListWithSpotLight(aMetaDataItem as string, aParam as string, startDir as {alias, string})
  set sDirText to quoted form of POSIX path of startDir
  
set shellText to “/usr/bin/mdfind ’” & aMetaDataItem & ” == \”" & aParam & “\”’ -onlyin “ & sDirText
  
try
    set aRes to do shell script shellText
  on error
    return {}
  end try
  
set pList to paragraphs of aRes
  
set aList to {}
  
repeat with i in pList
    set aPath to POSIX file i
    
set aPath to aPath as alias
    
set the end of aList to aPath
  end repeat
  
return aList
end getFileListWithSpotLight

★Click Here to Open This Script 

2014/12/01 2D Listを文字列長でソート

ASObjCExtras.framework v1.22で追加された、2D Listの文字列長によるソートを行うAppleScriptです。

AppleScript名:sortByLength_asoc
use AppleScript version “2.4″
use framework “Foundation”
use framework “ASObjCExtras” –ver 1.22 or later
use scripting additions

set aList to {{“Apple”, 1}, {“Orange”, 2}, {“Banana”, 100}, {“Meron”, 2}, {“Strawberry”, 0.1}, {“Lemon”, 3}, {“Takaaki Naganoya”, 5}, {“Piyomaru Software”, 11}}

set sortIndexes to {0} –Key Item id: begin from 0
set sortOrders to {true} –Ascending
set sortTypes to {“compare:”}
set sortKeys to {“length”, “self”}

set resArray to (current application’s SMSFord’s subarraysIn:(aList) sortedByIndexes:sortIndexes ascending:sortOrders sortTypes:sortTypes sortKeys:sortKeys |error|:(missing value))
set resList to resArray’s ASify() as list

–> {{”Apple”, 1}, {”Meron”, 2}, {”Lemon”, 3}, {”Orange”, 2}, {”Banana”, 100}, {”Strawberry”, 0.1}, {”Takaaki Naganoya”, 5}, {”Piyomaru Software”, 11}}

set sortIndexes to {0} –Key Item id: begin from 0
set sortOrders to {false} –Descending
set sortTypes to {“compare:”}
set sortKeys to {“length”, “self”}

set resArray to (current application’s SMSFord’s subarraysIn:(aList) sortedByIndexes:sortIndexes ascending:sortOrders sortTypes:sortTypes sortKeys:sortKeys |error|:(missing value))
set resList to resArray’s ASify() as list

–> {{”Piyomaru Software”, 11}, {”Takaaki Naganoya”, 5}, {”Strawberry”, 0.1}, {”Orange”, 2}, {”Banana”, 100}, {”Apple”, 1}, {”Meron”, 2}, {”Lemon”, 3}}

★Click Here to Open This Script 

2014/11/26 1D Listを文字列長でソート v2

1D Listの文字列長によるソートの改良版です。文字列長に着目してソートする場合には、同一長の文字列がソートされている必要はとくにないのですが、同一文字列長の場合にソートを行っています。

1D Listの文字列長ソートを掲載していたら、Shane Stanleyからツッコミが。

「それ、間違いじゃないけど文字列長でソートして、同時に(文字の)ソート順を指定しておいた方がいいよ(意訳)」

たしかに。文字列長でソートする用途しか考えていなかったので、文字列順でソートされていることは重視していませんでしたが、文字列順にソートされていた方がいい感じです。

文字列長でソートする用途というのは・・・Mail.appでSubjectに含まれるキーワードでサブフォルダに仕分けを行うさいに、文字列の長いものを優先して(長い→短い の順に)フォルダとの照合を行うロジックをAppleScriptで作って(&使って)いるためです。

このロジックを使用しているため、「Mountain Lion」のほうが「Lion」よりも優先して処理されることになり、(ほぼ)思い通りの処理をおこなえています。

US AppleがホスティングしているMailing Listにほぼすべて入っており(いくつか、US現地の教育関係者と政府関係者しか入れないMLからは丁重に追い出されましたが)、MLのログをこのようにキーワードごとに細分化したサブフォルダ(複数キーワードをカンマで区切って、類義語を列挙する仕様)に再振り分けしています。

mails.png

AppleScript名:1D Listを文字列長でソート v2
– Created 2014-11-25 by Shane Stanley
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “ASObjCExtras”

set aList to {“Apple”, “Orange”, “Banana”, “Meron”, “Strawberry”, “Lemon”, “Takaaki Naganoya”, “Piyomaru Software”}
set bList to sort1DListByStringLength(aList as list, true) of me –昇順
–> {”Apple”, “Meron”, “Lemon”, “Orange”, “Banana”, “Strawberry”, “Takaaki Naganoya”, “Piyomaru Software”}–v1
–> {”Apple”, “Lemon”, “Meron”, “Banana”, “Orange”, “Strawberry”, “Takaaki Naganoya”, “Piyomaru Software”}–v2

set cList to sort1DListByStringLength(aList as list, false) of me –降順
–> {”Piyomaru Software”, “Takaaki Naganoya”, “Strawberry”, “Orange”, “Banana”, “Apple”, “Meron”, “Lemon”}–v1
–> {”Piyomaru Software”, “Takaaki Naganoya”, “Strawberry”, “Banana”, “Orange”, “Apple”, “Lemon”, “Meron”}–v2

–1D Listを文字列長でソート v2
on sort1DListByStringLength(aList as list, sortOrder as boolean)
  set aArray to current application’s NSArray’s arrayWithArray:aList
  
set desc1 to current application’s NSSortDescriptor’s sortDescriptorWithKey:“length” ascending:sortOrder
  
set desc2 to current application’s NSSortDescriptor’s sortDescriptorWithKey:“self” ascending:true selector:“localizedCaseInsensitiveCompare:”
  
set bArray to aArray’s sortedArrayUsingDescriptors:{desc1, desc2}
  
set bList to (bArray’s ASify()) as list
  
return bList
end sort1DListByStringLength

★Click Here to Open This Script 

2014/11/25 1D Listをユニーク化してソート

1D Listをユニーク化してソートするAppleScriptです。

以前に、いろいろ1D Listのユニーク化の方法について検討していましたが、処理後のソート状態が「不定」であるため、決め手に欠けるみたいな話になっていました。

ただ、ソート状態が「不定」なら、明示的にソートすればいいだけの話で、そのように処理を足してやりました。

AppleScript名:1D Listをユニーク化してソート
– Created 2014-11-25 by Takaaki Naganoya
– 2014 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "ASObjCExtras"

set aList to {100, 10, 1, 90, 300, 300}

set aRes to uniquifyAndSort1DList(aList, true)
–> {1, 10, 90, 100, 300}

–1D Listをユニーク化してソート
on uniquifyAndSort1DList(theList as list, aBool as boolean)
  
  
set aArray to current application’s NSArray’s arrayWithArray:theList
  
set bArray to aArray’s valueForKeyPath:"@distinctUnionOfObjects.self"
  
set aDdesc to current application’s NSSortDescriptor’s sortDescriptorWithKey:"self" ascending:aBool selector:"compare:"
  
set cArray to bArray’s sortedArrayUsingDescriptors:{aDdesc}
  
  
set bList to (cArray’s ASify()) as list
  
  
return bList
end uniquifyAndSort1DList

★Click Here to Open This Script 

2014/11/25 1D Listを文字列長でソート

文字列だけで構成された1D Listを、各要素の文字列の長さに注目してソートするAppleScriptです。

以前に作成したルーチンのASOC版です。ただ・・・一般的には1D Listだけで処理するなんてことはほとんどなく、いくつかのデータを一緒にした2D Listで処理することになります。

2D Listの文字列長ソートも書いて(掲載しかけ)たのですが、ASObjCExtras.frameworkが2D Listのソート時に「length」でのソートをサポートしていないことがFrameworkのヘッダーファイルに書いてあったので、おクラ入りしました。残念!

AppleScript名:1D Listを文字列長でソート
– Created 2014-11-25 by Takaaki Naganoya
– 2014 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “ASObjCExtras”

set aList to {“Apple”, “Orange”, “Banana”, “Meron”, “Strawberry”, “Lemon”, “Takaaki Naganoya”, “Piyomaru Software”}
set bList to sort1DListByStringLength(aList as list, true) of me –昇順
–> {”Apple”, “Meron”, “Lemon”, “Orange”, “Banana”, “Strawberry”, “Takaaki Naganoya”, “Piyomaru Software”}

set cList to sort1DListByStringLength(aList as list, false) of me –降順
–> {”Piyomaru Software”, “Takaaki Naganoya”, “Strawberry”, “Orange”, “Banana”, “Apple”, “Meron”, “Lemon”}

–1D Listを文字列長でソート
on sort1DListByStringLength(aList as list, sortOrder as boolean)
  set aArray to current application’s NSArray’s arrayWithArray:aList
  
set descs to current application’s NSArray’s arrayWithObject:(current application’s NSSortDescriptor’s sortDescriptorWithKey:“length” ascending:sortOrder)
  
  
set bArray to aArray’s sortedArrayUsingDescriptors:descs
  
set bList to (bArray’s ASify()) as list
  
return bList
end sort1DListByStringLength

★Click Here to Open This Script 

2014/11/21 レコードのリストをソート(asoc)

レコードのリストをソートするAppleScriptです。

{{aName:”piyoko”, aVal:100}, {aName:”piyomaru”, aVal:80}, {aName:”piyoo”, aVal:10}}

のようなレコードをリスト化したデータに対して、レコードの属性ラベルを指定してその値でソートを行います。

AppleScript名:asoc_レコードのリストをソート
– Created 2014-11-21 by Takaaki Naganoya
– 2014 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “ASObjCExtras”

set aRecList to {{aName:“piyoko”, aVal:100}, {aName:“piyomaru”, aVal:80}, {aName:“piyoo”, aVal:10}}

set bList to sortRecListByLabel(aRecList, “aVal”, true) of me –昇順ソート
–> {{aName:”piyoo”, aVal:10}, {aName:”piyomaru”, aVal:80}, {aName:”piyoko”, aVal:100}}

set bList to sortRecListByLabel(aRecList, “aVal”, false) of me –降順ソート
–> {{aName:”piyoko”, aVal:100}, {aName:”piyomaru”, aVal:80}, {aName:”piyoo”, aVal:10}}

–リストに入れたレコードを、指定の属性ラベルの値でソート
on sortRecListByLabel(aRecList, aLabelStr, ascendF)
  set aArray to current application’s NSArray’s arrayWithArray:aRecList
  
  
set sortDesc to current application’s NSSortDescriptor’s alloc()’s initWithKey:aLabelStr ascending:ascendF
  
set sortDescArray to current application’s NSArray’s arrayWithObjects:sortDesc
  
set sortedArray to aArray’s sortedArrayUsingDescriptors:sortDescArray
  
  
set bList to (sortedArray’s ASify()) as list
  
return bList
end sortRecListByLabel

★Click Here to Open This Script 

2014/11/08 ASObjCExtras.frameworkのつかいかた〜2D Listのソート

ASObjCExtras.frameworkで2DのListをソートする機能を利用してみました。

AppleScriptの世界でよく利用される、ネストしたリスト、2次元配列とか2D Listとかいろいろ呼び方はありますが・・・これをソートするのにCocoaの機能を呼び出したい。

・・・のですが、なかなか同じようなものが見つからない。むしろ、Recordのソートなんかは手軽に行えるんですが、2D Listのソートがなかなか。

というニーズを受けて(?)、ASObjCExtras.frameworkの中に当たり前のように2D Listのソート機能が用意されています。

AppleScriptだけで組んだ場合と比較して(10万レコード時で)、6倍ぐらい高速に処理できます(MacBook Pro Retina Mid 2012にて計測)。

念のために、ベンチマークに用いたASOCのプログラムとAppleScriptのプログラムの両方を掲載しておきます。

graph1.png

10万レコードの2D Listの場合、AppleScriptだけでは40秒近くかかるところが7秒。そこまで大量のデータを処理するケースは少ないですが、高速なソートルーチンが使えるのであれば、使えたほうがいいに決まっています。

AppleScript名:ASObjCExtrasのじっけん_2Dリストのソート (asoc)
use AppleScript version “2.4″
use framework “Foundation”
use framework “ASObjCExtras”
use scripting additions

script orig
  property aList : {}
end script

set aList of orig to {}

set aList to {}
repeat 10000 times
  set the end of aList of orig to {“a”, random number from 1 to 10, random number from 1 to 10, random number from 1 to 100}
end repeat

set a1Dat to current application’s NSDate’s timeIntervalSinceReferenceDate()

–2D Listのソート
set sortIndexes to {1, 2, 3} –Key Item id: begin from 0
set sortOrders to {true, true, true}
set sortTypes to {“compare:”, “compare:”, “compare:”}
set resList to (current application’s SMSFord’s subarraysIn:(aList of orig) sortedByIndexes:sortIndexes ascending:sortOrders sortTypes:sortTypes |error|:(missing value)) as list

set b1Dat to current application’s NSDate’s timeIntervalSinceReferenceDate()
set c1Dat to b1Dat - a1Dat

return c1Dat

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

AppleScript名:2dlist_sort_as
use AppleScript version “2.4″
use framework “Foundation”
use scripting additions

script orig
  property aList : {}
end script

set aList of orig to {}

–複数キーによるソートテスト3(複数キーを許容)
set aList to {}
repeat 10000 times
  set the end of aList of orig to {“a”, random number from 1 to 10, random number from 1 to 10, random number from 1 to 100}
end repeat

set a1Dat to current application’s NSDate’s timeIntervalSinceReferenceDate()

set resList to multiKeySortDescending(aList of orig, {2, 3, 4}) of multiKeySort

set b1Dat to current application’s NSDate’s timeIntervalSinceReferenceDate()
set c1Dat to b1Dat - a1Dat

return c1Dat

–複数キーによるソート
script multiKeySort
  
  
script spd
    property aList : {} –ソート対象の2D Listが入る
    
property bList : {} –1次キーでソートした結果が入る
    
property cList : {} –1次キーでソートした結果のうち、1次キーで同じ値が連続する範囲が入る {{1,3},{10,20}}
    
    
property dList : {} –2次キーでソートする対象の一部のリストが入る(ワーク用)
    
property eList : {} –2次キーでソートした結果のリストが入る(ワーク用)
  end script
  
  
  
–複数キー(Primary, Secondary……)による降順ソート。キーは指定用のリストに入れる
  
on multiKeySortDescending(aList, keyList)
    
    
–Initialize
    
set aList of spd to {}
    
set bList of spd to {}
    
    
    
–Param Check
    
set aLen to length of keyList
    
if class of keyList is not equal to list then
      return false –キー値のリストがlistではなかった場合
    end if
    
    
–ソート対象の2D Listの要素数を取得
    
set tmpLen to length of first item of aList
    
repeat with i in keyList
      if i > tmpLen then
        return false –キー値として指定した内容が、ソート対象のリストの要素数よりも大きかった場合
      end if
    end repeat
    
    
    
set firstKeyNo to first item of keyList
    
    
–1次キーで2D Listをソート(降順)
    
set bList of spd to shellSortListDescending(aList, firstKeyNo) of me
    
    
–複数キーによるソート検証および実行ループ
    
repeat with iii from 1 to aLen - 1
      
      
set cList of spd to {}
      
set dList of spd to {}
      
set eList of spd to {}
      
      
–n次キーの値が連続する箇所を探す
      
set curData to missing value
      
      
set sucF to false –データ連続箇所検出中フラグ(false=非連続、true=連続中)
      
set biginItem to 0
      
set endItem to 0
      
      
set itemC to 0
      
      
repeat with i in bList of spd
        set thisData to item (item iii of keyList) of i –n次キー
        
        
–現在の値と前の値が等しい(連続箇所を検出した、あるいは連続箇所の中にいる)
        
if curData = thisData then
          
          
if sucF = false then
            set biginItem to itemC
            
set sucF to true
          else if sucF = true then
            –連続箇所の検索継続中、何もしない
          end if
          
        else
          –現在の値と前の値が等しくない(連続していない、あるいは連続箇所の末尾を検出した)
          
if sucF = true then
            set the end of cList of spd to {biginItem, itemC}
            
set sucF to false
          end if
          
          
set curData to thisData
          
        end if
        
        
set itemC to itemC + 1
        
      end repeat
      
      
–n次キーの連続状態の検出中のままリスト末尾に来た場合には、最終データを出力する
      
if sucF = true and curData = thisData then
        set the end of cList of spd to {biginItem, itemC}
      end if
      
      
      
–n次キーによる重複箇所がない場合には、n次キーによるソート結果をそのまま返す
      
if cList of spd = {} then
        return bList of spd
      end if
      
      
–n+1次キーによる部分ソートし直し
      
repeat with i in cList of spd
        set {tmpB, tmpE} to i
        
        
copy items tmpB thru tmpE of (bList of spd) to (dList of spd)
        
set (eList of spd) to shellSortListDescending((dList of spd), (item (iii + 1) of keyList)) of me
        
        
set tmpCounter to 1
        
repeat with ii from tmpB to tmpE
          copy item tmpCounter of (eList of spd) to item ii of (bList of spd)
          
set tmpCounter to tmpCounter + 1
        end repeat
      end repeat
      
    end repeat
    
    
return (bList of spd)
    
  end multiKeySortDescending
  
  
–シェルソートで入れ子のリストを降順ソート
  
on shellSortListDescending(aSortList, aKeyItem)
    script oBj
      property list : aSortList
    end script
    
set len to count oBj’s list’s items
    
set gap to 1
    
repeat while (gap len)
      set gap to ((gap * 3) + 1)
    end repeat
    
repeat while (gap > 0)
      set gap to (gap div 3)
      
if (gap < len) then
        repeat with i from gap to (len - 1)
          set temp to oBj’s list’s item (i + 1)
          
set j to i
          
repeat while ((j gap) and (contents of item aKeyItem of (oBj’s list’s item (j - gap + 1)) < item aKeyItem of temp))
            set oBj’s list’s item (j + 1) to oBj’s list’s item (j - gap + 1)
            
set j to j - gap
          end repeat
          
set oBj’s list’s item (j + 1) to temp
        end repeat
      end if
    end repeat
    
return oBj’s list
  end shellSortListDescending
  
end script

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

2014/10/30 ASからASOCのハンドラを呼び出す際には

昨日の問題の解決・・・小数を含む数値のリストをASOC(「AppleScriptObjC」あるいは「AppleScript/Objective-C」と呼ばれる)のハンドラに渡してソートさせた場合に、小数を含む値に丸め誤差が発生するという問題についてです。

これまでにも、Xcode上でASOCのプログラム(GUIベースのアプリケーション)は作ってきましたが、ASOCのハンドラ同士で呼び出しを行う際には極力文字列でデータをやりとりし、AppleScript側からASOC側を呼び出すことはしてきませんでした。

OS X 10.9までは、スクリプトエディタ上でもASOCのプログラムは書けたものの、あくまでアプレット上で完全にプログラムが分断された状態で動いていたために、見えてこなかった問題点でもあります。

つまり、この問題はOS X 10.10で1本のScript中で通常のAppleScriptとASOCが混在する状況が生まれたために見えてきた問題ともいえます(個人的に、OS X 10.9までのScript Editor上で記述するASOC(Interface BuilderでGUIが作れない)にあまり魅力を感じていなかったので、全然使っていなかったし)。

xcode.png

# Xcode上でも「小数点を含む数値リスト」を処理した場合には、同様の問題が出ました。Scripting Bridgeそのもののバグっぽいですね。Appleにバグレポートを書いて担当者を説得するにも時間がかかるので、目下解決策を検証中です。解決はしたのですが、自分ひとりだけでは済まないので、、、

調査したところ、list→NSArrayの型変換では問題は発生していないようでしたが、ASOCのハンドラからNSArray→listの型変換を行って値を返す際に問題が発生していました。

NSArray→listの型変換そのものはScripting Bridgeの処理系が行っているため、現状ではどーーにもなりません(Scripting Bridgeのバグ)が、「数値リスト」ではなく「数字のテキストのリスト」を扱うようにすれば、数値リストの型変換にともなう丸め誤差の発生を回避できます。

つまり、数値文字列のリストをASOC側に渡して、ソーティング(大小比較)自体を「数値として評価」すればOK。

というわけで、ASOC側には「数値リスト」ではなく「数字の文字列リスト」を渡し、ASOC側でそれぞれの要素を「数値として大小比較」するようにしてみました。念のため、有効桁数の多いfloatValueとして数値評価させています。

だいたい、AppleScriptObjCでプログラムを組む場合には、User Interface系をAppleScriptObjCで実装し、メインロジック部分は別途「通常のAppleScript」で組むことで、メンテナンス性を高めつつ、使いやすさを向上させる・・・といったケースが多いところです。

このため、AppleScriptObjCからAppleScriptを呼び出す場合、メニューやラジオボタンのID(整数)であったり、ファイルのパス(文字列)であったりと、今回の問題にひっかからない利用の仕方がほとんどでした。

OS X 10.10の「通常のAppleScriptの中にAppleScriptObjCを書けるようにする」という拡張により、これまでに少なかった「AppleScriptからAppleScriptObjCの機能を利用し、値を受け取る」という処理パターンが増え、問題点が露見したのでしょう。

AppleScript名:asoc_sorting_number_list_v2
use AppleScript version “2.4″
use framework “Foundation”
use scripting additions

set aList to {“-1.1″, “1″, “10″, “0.1″, “1.1″, “1.12″, “-1″, “-100″} –Original List
set aRes to sort1DNumList_ascOrder_(aList, true)
–> {”-100″, “-1.1″, “-1″, “0.1″, “1″, “1.1″, “1.12″, “10″}–sort ascending

set bRes to sort1DNumList_ascOrder_(aList, false)
–> {”10″, “1.12″, “1.1″, “1″, “0.1″, “-1″, “-1.1″, “-100″}–sort descending

–Sort 1-Dimension List(String Number List)
on sort1DNumList:theList ascOrder:aBool
  tell current application’s NSSet to set theSet to setWithArray_(theList)
  
tell current application’s NSSortDescriptor to set theDescriptor to sortDescriptorWithKey_ascending_(“floatValue”, aBool)
  
set sortedList to theSet’s sortedArrayUsingDescriptors:{theDescriptor}
  
return (sortedList) as list
end sort1DNumList:ascOrder:

(*
–Sort 1-Dimension List(Number List)
on sort1DNumList2:theList ascOrder:aBool
  set anArray to current application’s NSArray’s arrayWithArray:theList
  set sortedList to anArray’s sortedArrayUsingSelector:(”numberCompare:”)
  return (sortedList) as list
end sort1DNumList2:ascOrder:
*)

–Sort 1-Dimension List(String List)
on sort1DList:theList ascOrder:aBool
  set aDdesc to current application’s NSSortDescriptor’s sortDescriptorWithKey:“self” ascending:aBool selector:“localizedCaseInsensitiveCompare:”
  
set theArray to current application’s NSArray’s arrayWithArray:theList
  
return (theArray’s sortedArrayUsingDescriptors:{aDdesc}) as list
end sort1DList:ascOrder:

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

2014/10/29 AppleScriptからASOCのソートルーチンを利用する上での注意事項

OS X 10.10で一般のAppleScriptとASOCを普通にまじえて記述できるようになったため、いろいろと試してみました。

すると、ソートについていろいろと注意すべき点がわかってきたのでまとめてみます。

文字列のソート順序が違う

ためしにさまざまな文字を与えて並び順を調べてみたら、微妙に違うことがわかりました。

テストデータとして、

set aList to {”ん”, “ば”, “は”, “ぱ”, “あ”, “ま”, “ぁ”, “ア”, “か”, “ァ”}

のようなデータを用意し、昇順ソートを指定した際に、

AS: {”あ”, “ァ”, “ぁ”, “ア”, “か”, “は”, “ば”, “ぱ”, “ま”, “ん”}
ASOC:{”あ”, “ぁ”, “ア”, “ァ”, “か”, “は”, “ば”, “ぱ”, “ま”, “ん”}

と、文字の並び順が微妙に異なります。ASOC側でもうちょっとソートのパラメータを指定すると結果が変わったりしてきそうですが、とりあえず現状はこんな感じです。

どちらかといえば、ASだけで行うソートの方に不自然さを感じるぐらいですし、いまどき半角カタカナでもないでしょうから無視できそうですが・・・問題は次です。

小数を含む数値リストをソートすると丸め誤差が出る

小数を含む数値のリスト、

set aList to {1, 1, 10, 0.1, 1.1, -1, -100}

をデータとして与えてソートしてみたところ、返ってきた結果は、

–> {-100, -1, 0.10000000149, 1, 1.100000023842, 10}

微妙に値が変わってしまいました

回避策についてはいろいろ調べていますが、(US AppleのAppleScript Users MLに久しぶりに投稿してShane Stanleyに教えてもらいました)小数の値を含む数値リストのやりとりをするのは得策ではなさそうな雰囲気です(いまのところ)。

少なくとも、ASから数値をASOCのハンドラに渡しただけでは変わらないようです。

いろいろ試してみたところ、ハンドラ側でNSNumberに変換して処理を行い、結果を戻すさいに「as number」とか「as real」とかで型変換して返すときに丸め誤差が出てしまう模様です。

数値のパラメータであれば、ASから数値をASOCハンドラに渡して、丸め誤差を出さずに返すことはできたのですが、リストに入れてしまうとお手上げの状態。

まだ、試行錯誤を行ってみる必要があるようです。

AppleScript名:asocで1D Listをソート
use AppleScript version “2.4″
use framework “Foundation”
use scripting additions

–昇順ソート(ASOC)
set aList to {“ん”, “ば”, “は”, “ぱ”, “あ”, “ま”, “ぁ”, “ア”, “か”, “ァ”}
set aRes to sort1DList_ascOrder_(aList, true)
–> {”あ”, “ぁ”, “ア”, “ァ”, “か”, “は”, “ば”, “ぱ”, “ま”, “ん”}

–降順ソート(ASOC)
set bRes to sort1DList_ascOrder_(aList, false)
–> {”ん”, “ま”, “ぱ”, “ば”, “は”, “か”, “あ”, “ぁ”, “ア”, “ァ”}

–昇順ソート(AppleScript)
set cRes to shellSortAscending(aList)
–> {”あ”, “ァ”, “ぁ”, “ア”, “か”, “は”, “ば”, “ぱ”, “ま”, “ん”}

–昇順ソート(AppleScript)
set dRes to shellSortDescending(aList)
–> {”ん”, “ま”, “ぱ”, “ば”, “は”, “か”, “ぁ”, “ア”, “あ”, “ァ”}

set aList to {“1″, “01″, “10″, “000.1″, “1.1″, “-1″, “-100″}
set aRes to sort1DList_ascOrder_(aList, true)
–> {”-1″, “-100″, “000.1″, “01″, “1″, “1.1″, “10″}

set aList to {1, 1, 10, 0.1, 1.1, -1, -100}
set aRes to sort1DNumList_ascOrder_(aList, true)
–> {-100, -1, 0.10000000149, 1, 1.100000023842, 10}

–1D List(数値)をsort / ascOrderがtrueだと昇順ソート、falseだと降順ソート
on sort1DNumList:theList ascOrder:aBool
  tell current application’s NSSet to set theSet to setWithArray_(theList)
  
tell current application’s NSSortDescriptor to set theDescriptor to sortDescriptorWithKey_ascending_(missing value, true)
  
set sortedList to theSet’s sortedArrayUsingDescriptors:{theDescriptor}
  
return (sortedList) as list
end sort1DNumList:ascOrder:

–1D List(文字)をsort / ascOrderがtrueだと昇順ソート、falseだと降順ソート
on sort1DList:theList ascOrder:aBool
  set aDdesc to current application’s NSSortDescriptor’s sortDescriptorWithKey:“self” ascending:aBool selector:“localizedCaseInsensitiveCompare:”
  
set theArray to current application’s NSArray’s arrayWithArray:theList
  
return (theArray’s sortedArrayUsingDescriptors:{aDdesc}) as list
end sort1DList:ascOrder:

–入れ子ではないリストの昇順ソート
on shellSortAscending(aSortList)
  script oBj
    property list : aSortList
  end script
  
set len to count oBj’s list’s items
  
set gap to 1
  
repeat while (gap len)
    set gap to ((gap * 3) + 1)
  end repeat
  
repeat while (gap > 0)
    set gap to (gap div 3)
    
if (gap < len) then
      repeat with i from gap to (len - 1)
        set temp to oBj’s list’s item (i + 1)
        
set j to i
        
repeat while ((j gap) and (oBj’s list’s item (j - gap + 1) > temp))
          set oBj’s list’s item (j + 1) to oBj’s list’s item (j - gap + 1)
          
set j to j - gap
        end repeat
        
set oBj’s list’s item (j + 1) to temp
      end repeat
    end if
  end repeat
  
return oBj’s list
end shellSortAscending

–入れ子ではないリストの降順ソート
on shellSortDescending(aSortList)
  script oBj
    property list : aSortList
  end script
  
set len to count oBj’s list’s items
  
set gap to 1
  
repeat while (gap len)
    set gap to ((gap * 3) + 1)
  end repeat
  
repeat while (gap > 0)
    set gap to (gap div 3)
    
if (gap < len) then
      repeat with i from gap to (len - 1)
        set temp to oBj’s list’s item (i + 1)
        
set j to i
        
repeat while ((j gap) and (oBj’s list’s item (j - gap + 1) < temp))
          set oBj’s list’s item (j + 1) to oBj’s list’s item (j - gap + 1)
          
set j to j - gap
        end repeat
        
set oBj’s list’s item (j + 1) to temp
      end repeat
    end if
  end repeat
  
return oBj’s list
end shellSortDescending

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

AppleScript名:asocで少数を含む数値を誤差のない形式でASに返す
–By Shane Stanley

use AppleScript version “2.4″
use framework “Foundation”
use scripting additions

set e to 0.1
set f to numberFloatWith_(e)
–> 0.1

on numberFloatWith:aNum
  set theNum to current application’s NSNumber’s numberWithFloat:aNum
  
return (theNum’s descriptionWithLocale:(current application’s NSLocale’s systemLocale())) as real
end numberFloatWith:

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

2014/10/28 OS X 10.10のasocで1D Listをソート

OS X 10.10のAppleScript処理系は、マルチコアのCPUを活かして高速に処理を行うように変更されているように見えます。実際、スクリプトエディタ上でAppleScriptを実行すると、

process.png

OS X 10.9までよりもはるかに多くの内部スレッドを生成し、マシンパワーを絞り出すようにチューニングされていることがうかがわれます。

OS X 10.10ではCocoaの機能を直接呼び出すAppleScriptObjCが気軽に使えるようになったため、処理パワーが必要とされるリスト(配列)のソートをAppleScriptObjCと通常のAppleScriptで実行して処理時間を計測してみました。

処理対象の1-Dimension List(1次元配列)は10万アイテムで構成(すべて固定長の乱数を文字列化したもの)。

実行環境は、MacBook Pro Retina 2012(Core i7 2.66GHz x 4 core , 8GB RAM)およびMacBook Air 2011(Core i5 1.6GHz ,4GB RAM)です。

sort_res.png

結果は、ASOCの方が6〜18倍程度高速でした。これは、本機能を使わない手はありません。

ただ、調子に乗って100万アイテムのリストを作らせてみたら・・・RAMを使いつぶしたりスクリプトエディタを無反応にさせたりしたため、10万アイテム程度がこのぐらいのスペックのコンピュータ上でAppleScriptでデータ処理を行わせる場合には適正レベルといえるでしょう。

ぜひ、2D Listのソートもやらせたいですね。

AppleScript名:asocで1D Listをソート(じっけん)
use AppleScript version “2.4″
use framework “Foundation”
use scripting additions

script spd
  property aList : {}
end script

–テスト用データリストの作成(10万アイテム)
set (aList of spd) to {}
repeat with i from 1 to 100000
  set the end of (aList of spd) to (random number from 10000 to 99999) as string
end repeat

–昇順ソートの時間計測
set a1Dat to current date

set aRes to sort1DList_ascOrder_((aList of spd), true)
set item1 to contents of (items -9 thru -1 of aRes)

set b1Dat to current date
set c1Dat to b1Dat - a1Dat

–降順ソートの時間計測
set a2Dat to current date

set aRes to sort1DList_ascOrder_((aList of spd), false)
set item1 to contents of (items -9 thru -1 of aRes)

set b2Dat to current date
set c2Dat to b2Dat - a2Dat

return {c1Dat, c2Dat}
–> {1, 1}

–1D Listをsort / ascOrderがtrueだと昇順ソート、falseだと降順ソート
on sort1DList:theList ascOrder:aBool
  set aDdesc to current application’s NSSortDescriptor’s sortDescriptorWithKey:“self” ascending:aBool selector:“localizedCaseInsensitiveCompare:”
  
set theArray to current application’s NSArray’s arrayWithArray:theList
  
return (theArray’s sortedArrayUsingDescriptors:{aDdesc}) as list
end sort1DList:ascOrder:

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

AppleScript名:asで1D Listをソート(じっけん)
use AppleScript version “2.4″
use scripting additions

script spd
  property aList : {}
end script

–テスト用データリストの作成(10万アイテム)
set (aList of spd) to {}
repeat with i from 1 to 100000
  set the end of (aList of spd) to (random number from 10000 to 99999) as string
end repeat

–昇順ソートの時間計測
set a1Dat to current date

set aRes to shellSortAscending(aList of spd)

set b1Dat to current date
set c1Dat to b1Dat - a1Dat

–降順ソートの時間計測
set a2Dat to current date

set aRes to shellSortDescending(aList of spd)

set b2Dat to current date
set c2Dat to b2Dat - a2Dat

return {c1Dat, c2Dat}
–> {19, 8}

–入れ子ではないリストの昇順ソート
on shellSortAscending(aSortList)
  script oBj
    property list : aSortList
  end script
  
set len to count oBj’s list’s items
  
set gap to 1
  
repeat while (gap len)
    set gap to ((gap * 3) + 1)
  end repeat
  
repeat while (gap > 0)
    set gap to (gap div 3)
    
if (gap < len) then
      repeat with i from gap to (len - 1)
        set temp to oBj’s list’s item (i + 1)
        
set j to i
        
repeat while ((j gap) and (oBj’s list’s item (j - gap + 1) > temp))
          set oBj’s list’s item (j + 1) to oBj’s list’s item (j - gap + 1)
          
set j to j - gap
        end repeat
        
set oBj’s list’s item (j + 1) to temp
      end repeat
    end if
  end repeat
  
return oBj’s list
end shellSortAscending

–入れ子ではないリストの降順ソート
on shellSortDescending(aSortList)
  script oBj
    property list : aSortList
  end script
  
set len to count oBj’s list’s items
  
set gap to 1
  
repeat while (gap len)
    set gap to ((gap * 3) + 1)
  end repeat
  
repeat while (gap > 0)
    set gap to (gap div 3)
    
if (gap < len) then
      repeat with i from gap to (len - 1)
        set temp to oBj’s list’s item (i + 1)
        
set j to i
        
repeat while ((j gap) and (oBj’s list’s item (j - gap + 1) < temp))
          set oBj’s list’s item (j + 1) to oBj’s list’s item (j - gap + 1)
          
set j to j - gap
        end repeat
        
set oBj’s list’s item (j + 1) to temp
      end repeat
    end if
  end repeat
  
return oBj’s list
end shellSortDescending

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

2014/01/20 複数キーによるソートテスト(キー数無制限)

複数キーによる2D Listのソーティング(降順)のテスト実装を行ったAppleScriptです。

プログラムを眺めていたら、思ったよりも簡単に複数(上限なし)キー対応できることが分ったので、その場でさくっと書いてしまいました。

キー数にとくに上限はありませんが、キー数を増やせば、それだけ処理に時間がかかります。

MacBook Pro Retina(2012)Core i7 2.66GHzの環境で、リスト要素数が10,000でキーが3つだと2秒程度で処理が終了します。

# 後日、AppleStoreの店頭で3.7GHz Quad CoreのMacProで実験してみたところ、1秒だったり2秒だったりと、さほどMacBook Pro Retinaと差はありませんでした(1秒だと倍ぐらい速いことになりますが)

処理内容はおおまかにいえば……

1パス目:Primary Keyを使って2D Listをソート
2パス目:ソートずみリストのNth Keyの値を順にすべてチェック。同じ値が続く箇所をピックアップして記録
3パス目:2パス目でNth Keyの同値の内容が連続する箇所が存在していた場合には、重複部分のみ取り出して、N+1th Keyで再度ソートしてソート結果を(部分的に)差し替える

(以下、2&3パスを繰り返し)

キーはPrimaryもSecondary以下もすべて1つの1次元リスト(配列)に入れて指定します。キー値リストの内容がソート対象2D Listデータのデータ個数を超えないかどうかはチェックしていますが、キー値リストの内容の重複まではチェックしていません(キー値リストの中に0を入れてもダメ)。

スクリプト名:複数キーによるソートテスト3
script orig
  property aList : {}
end script

set aList of orig to {}

–複数キーによるソートテスト3(複数キーを許容)
set aList to {}
repeat 10000 times
  set the end of aList of orig to {“a”, random number from 1 to 10, random number from 1 to 10, random number from 1 to 100}
end repeat

set sDat to current date
set resList to multiKeySortDescending(aList of orig, {2, 3, 4}) of multiKeySort
set eDat to current date

return (eDat - sDat)

–複数キーによるソート
script multiKeySort
  
  
script spd
    property aList : {} –ソート対象の2D Listが入る
    
property bList : {} –1次キーでソートした結果が入る
    
property cList : {} –1次キーでソートした結果のうち、1次キーで同じ値が連続する範囲が入る {{1,3},{10,20}}
    
    
property dList : {} –2次キーでソートする対象の一部のリストが入る(ワーク用)
    
property eList : {} –2次キーでソートした結果のリストが入る(ワーク用)
  end script
  
  
  
–複数キー(Primary, Secondary……)による降順ソート。キーは指定用のリストに入れる
  
on multiKeySortDescending(aList, keyList)
    
    
–Initialize
    
set aList of spd to {}
    
set bList of spd to {}
    
    
    
–Param Check
    
set aLen to length of keyList
    
if class of keyList is not equal to list then
      return false –キー値のリストがlistではなかった場合
    end if
    
    
–ソート対象の2D Listの要素数を取得
    
set tmpLen to length of first item of aList
    
repeat with i in keyList
      if i > tmpLen then
        return false –キー値として指定した内容が、ソート対象のリストの要素数よりも大きかった場合
      end if
    end repeat
    
    
    
set firstKeyNo to first item of keyList
    
    
–1次キーで2D Listをソート(降順)
    
set bList of spd to shellSortListDescending(aList, firstKeyNo) of me
    
    
–複数キーによるソート検証および実行ループ
    
repeat with iii from 1 to aLen - 1
      
      
set cList of spd to {}
      
set dList of spd to {}
      
set eList of spd to {}
      
      
–n次キーの値が連続する箇所を探す
      
set curData to missing value
      
      
set sucF to false –データ連続箇所検出中フラグ(false=非連続、true=連続中)
      
set biginItem to 0
      
set endItem to 0
      
      
set itemC to 0
      
      
repeat with i in bList of spd
        set thisData to item (item iii of keyList) of i –n次キー
        
        
–現在の値と前の値が等しい(連続箇所を検出した、あるいは連続箇所の中にいる)
        
if curData = thisData then
          
          
if sucF = false then
            set biginItem to itemC
            
set sucF to true
          else if sucF = true then
            –連続箇所の検索継続中、何もしない
          end if
          
        else
          –現在の値と前の値が等しくない(連続していない、あるいは連続箇所の末尾を検出した)
          
if sucF = true then
            set the end of cList of spd to {biginItem, itemC}
            
set sucF to false
          end if
          
          
set curData to thisData
          
        end if
        
        
set itemC to itemC + 1
        
      end repeat
      
      
–n次キーの連続状態の検出中のままリスト末尾に来た場合には、最終データを出力する
      
if sucF = true and curData = thisData then
        set the end of cList of spd to {biginItem, itemC}
      end if
      
      
      
–n次キーによる重複箇所がない場合には、n次キーによるソート結果をそのまま返す
      
if cList of spd = {} then
        return bList of spd
      end if
      
      
–n+1次キーによる部分ソートし直し
      
repeat with i in cList of spd
        set {tmpB, tmpE} to i
        
        
copy items tmpB thru tmpE of (bList of spd) to (dList of spd)
        
set (eList of spd) to shellSortListDescending((dList of spd), (item (iii + 1) of keyList)) of me
        
        
set tmpCounter to 1
        
repeat with ii from tmpB to tmpE
          copy item tmpCounter of (eList of spd) to item ii of (bList of spd)
          
set tmpCounter to tmpCounter + 1
        end repeat
      end repeat
      
    end repeat
    
    
return (bList of spd)
    
  end multiKeySortDescending
  
  
–シェルソートで入れ子のリストを降順ソート
  
on shellSortListDescending(aSortList, aKeyItem)
    script oBj
      property list : aSortList
    end script
    
set len to count oBj’s list’s items
    
set gap to 1
    
repeat while (gap len)
      set gap to ((gap * 3) + 1)
    end repeat
    
repeat while (gap > 0)
      set gap to (gap div 3)
      
if (gap < len) then
        repeat with i from gap to (len - 1)
          set temp to oBj’s list’s item (i + 1)
          
set j to i
          
repeat while ((j gap) and (contents of item aKeyItem of (oBj’s list’s item (j - gap + 1)) < item aKeyItem of temp))
            set oBj’s list’s item (j + 1) to oBj’s list’s item (j - gap + 1)
            
set j to j - gap
          end repeat
          
set oBj’s list’s item (j + 1) to temp
        end repeat
      end if
    end repeat
    
return oBj’s list
  end shellSortListDescending
  
end script

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

2014/01/20 複数キーによるソートテスト

複数キーによる2D Listのソーティング(降順)のテスト実装を行ったAppleScriptです。

2D Listのソートルーチンはいくつも存在しており、ユーザーによるスピードの向上が行われ、充実したものとなってきました。

ただし、複数キーによるソートルーチンについては、海外を探してはみたもののなかなか見つからず、「根気づよく探すよりも、自分で作った方が早い」という状況でした。

そこで、試験的に実装してみました。MacBook Pro Retina(2012)Core i7 2.66GHzの環境で、リスト要素数が1,000で約1秒以下、要素数を10,000にしても1秒程度(テストデータの作成で時間がかかっていました)。

最短で実装できることを重視したため、3パスでソートするようにしました。

1パス目:Primary Keyを使って2D Listをソート
2パス目:ソートずみリストのPrimary Keyの値を順にすべてチェック。同じ値が続く箇所をピックアップして記録
3パス目:2パス目でPrimary Keyの同値の内容が連続する箇所が存在していた場合には、重複部分のみ取り出して、Secondary Keyで再度ソートしてソート結果を(部分的に)差し替える

という処理にしてみました。

改良の方向性はいくつかあって……

(1)昇順、降順をパラメータで指定
(2)キーの個数を無限に増やせるようにする(再帰処理による?)
(3)スピードアップのために処理内容見直し
(4)Cocoaで処理(AppleScriptObjC)

など、いろいろ考えられます。

スクリプト名:複数キーによるソートテスト2
script orig
  property aList : {}
end script

set aList of orig to {}

–複数キーによるソートテスト2
set aList to {}
repeat 1000 times
  set the end of aList of orig to {"a", random number from 1 to 10, random number from 1 to 100}
end repeat

set sDat to current date
set resList to multiKeySortDescending(aList of orig, 2, 3) of multiKeySort
set eDat to current date

return (eDat - sDat)

–複数キーによるソート
script multiKeySort
  
  
script spd
    property aList : {} –ソート対象の2D Listが入る
    
property bList : {} –1次キーでソートした結果が入る
    
property cList : {} –1次キーでソートした結果のうち、1次キーで同じ値が連続する範囲が入る {{1,3},{10,20}}
    
    
property dList : {} –2次キーでソートする対象の一部のリストが入る(ワーク用)
    
property eList : {} –2次キーでソートした結果のリストが入る(ワーク用)
  end script
  
  
  
–複数キー(Primary, Secondary)による降順ソート
  
on multiKeySortDescending(aList, firstKeyNo, secondKeyNo)
    
    
–Initialize
    
set aList of spd to {}
    
set bList of spd to {}
    
set cList of spd to {}
    
set dList of spd to {}
    
set eList of spd to {}
    
    
–1次キーで2D Listをソート(降順)
    
set bList of spd to shellSortListDescending(aList, firstKeyNo) of me
    
    
    
–1次キーの値が連続する箇所を探す
    
set curData to missing value
    
    
set sucF to false –データ連続箇所検出中フラグ(false=非連続、true=連続中)
    
set biginItem to 0
    
set endItem to 0
    
    
set itemC to 0
    
    
repeat with i in bList of spd
      set thisData to item firstKeyNo of i
      
–set thisData to contents of item firstKeyNo of i
      
      
–現在の値と前の値が等しい(連続箇所を検出した、あるいは連続箇所の中にいる)
      
if curData = thisData then
        
        
if sucF = false then
          set biginItem to itemC
          
set sucF to true
        else if sucF = true then
          –連続箇所の検索継続中、何もしない
        end if
        
      else
        –現在の値と前の値が等しくない(連続していない、あるいは連続箇所の末尾を検出した)
        
if sucF = true then
          set the end of cList of spd to {biginItem, itemC}
          
set sucF to false
        end if
        
        
set curData to thisData
        
      end if
      
      
set itemC to itemC + 1
      
    end repeat
    
    
–1次キーの連続状態の検出中のままリスト末尾に来た場合には、最終データを出力する
    
if sucF = true and curData = thisData then
      set the end of cList of spd to {biginItem, itemC}
    end if
    
    
    
–1次キーによる重複箇所がない場合には、1次キーによるソート結果をそのまま返す
    
if cList of spd = {} then
      return bList of spd
    end if
    
    
–2次キーによる部分ソートし直し
    
repeat with i in cList of spd
      set {tmpB, tmpE} to i
      
      
copy items tmpB thru tmpE of (bList of spd) to (dList of spd)
      
set (eList of spd) to shellSortListDescending((dList of spd), secondKeyNo) of me
      
      
set tmpCounter to 1
      
repeat with ii from tmpB to tmpE
        copy item tmpCounter of (eList of spd) to item ii of (bList of spd)
        
set tmpCounter to tmpCounter + 1
      end repeat
    end repeat
    
    
    
return (bList of spd)
    
  end multiKeySortDescending
  
  
–シェルソートで入れ子のリストを降順ソート
  
on shellSortListDescending(aSortList, aKeyItem)
    script oBj
      property list : aSortList
    end script
    
set len to count oBj’s list’s items
    
set gap to 1
    
repeat while (gap len)
      set gap to ((gap * 3) + 1)
    end repeat
    
repeat while (gap > 0)
      set gap to (gap div 3)
      
if (gap < len) then
        repeat with i from gap to (len - 1)
          set temp to oBj’s list’s item (i + 1)
          
set j to i
          
repeat while ((j gap) and (contents of item aKeyItem of (oBj’s list’s item (j - gap + 1)) < item aKeyItem of temp))
            set oBj’s list’s item (j + 1) to oBj’s list’s item (j - gap + 1)
            
set j to j - gap
          end repeat
          
set oBj’s list’s item (j + 1) to temp
        end repeat
      end if
    end repeat
    
return oBj’s list
  end shellSortListDescending
  
end script

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

2013/05/10 数値の2D Listから最大値を求める v4

入れ子のリスト(2次元配列)から最大値を求めるAppleScriptです。

これまで利用していた「入れ子リストのフラット化(FlattenList)」のサブルーチンが、高速なのはいいものの、リスト中の要素がすべて文字列になってしまうので、再帰で処理するルーチンをみつけてきて使ってみました。

また、不要な(マヌケな)処理がややあったので、そこのあたりはバッサリ切っています。高速化のための努力はしていないので、要素数が数千(4000以上?)に上るような処理の場合には高速化対策を考えたほうがよいでしょう。

2DのList(2次元配列)と書いていましたが、動作原理上とくに2Dである必要はなく、3Dでも4Dでも大丈夫です。

スクリプト名:数値の2D Listから最大値を求める v4
–入れ子の数値リストの最大値を求める
set rlist to {{0, 0, 0, 0, 0, 0}, {0, 1, 1, 0, 0, 0}, {1, 0, 0, 0, 1, 0}, {1, 0, 0, 1, 1, 0}, {1, 1, 1, 1, 5, 0}, {0, 0, 1, 1, 0, 0}, {0, 0, 0, 0, 0, 0}}

set aRes to getMaxFrom2DList(rlist) of me
–> 5

–2D Listから最大値を求める
on getMaxFrom2DList(rlist)
  set r2List to FlattenList(rlist) of me –2Dリストを1Dに変換
  
set maxItem to contents of first item of shellSortDescending(r2List)
  
return maxItem
end getMaxFrom2DList

–再帰タイプのリストのフラット化
–http://rosettacode.org/wiki/Flatten_a_list#AppleScript
on FlattenList(aList)
  if class of aList is not list then
    return {aList}
  else if length of aList is 0 then
    return aList
  else
    return FlattenList(first item of aList) & (FlattenList(rest of aList))
  end if
end FlattenList

–入れ子ではないリストの降順ソート
on shellSortDescending(aSortList)
  script oBj
    property list : aSortList
  end script
  
set len to count oBj’s list’s items
  
set gap to 1
  
repeat while (gap len)
    set gap to ((gap * 3) + 1)
  end repeat
  
repeat while (gap > 0)
    set gap to (gap div 3)
    
if (gap < len) then
      repeat with i from gap to (len - 1)
        set temp to oBj’s list’s item (i + 1)
        
set j to i
        
repeat while ((j gap) and (oBj’s list’s item (j - gap + 1) < temp))
          set oBj’s list’s item (j + 1) to oBj’s list’s item (j - gap + 1)
          
set j to j - gap
        end repeat
        
set oBj’s list’s item (j + 1) to temp
      end repeat
    end if
  end repeat
  
return oBj’s list
end shellSortDescending

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

2013/04/25 並び順をインデックスリストで指定してソート

並び順をインデックスリストで指定して2Dのリストを任意の並び順でソートするAppleScriptです。

日本の47都道府県に関するデータを集め……たとえば、都道府県の人口であれば、人数の多い順からソートするとか、都道府県に付けられたJISコード(全国地方公共団体コード)でソートするなど、いくつか指標となるものはあります。

ただ……都道府県を北から南へ順に並べるような処理をしたい場合には、もうその「並び順自体をデータ化」して、そのとおりにソート対象データを並べ替えるほかありません。

かくして、そのようなサブルーチンを作ってみました。

AppleScript名:並び順をインデックスリストで指定してソート
–並び順を指定するインデックスリスト(この順番にソート対象データを並べ替える)
set indexList to {"北海道", "青森県", "岩手県", "秋田県", "宮城県", "山形県", "福島県", "新潟県", "富山県", "石川県", "福井県", "茨城県", "栃木県", "群馬県", "埼玉県", "千葉県", "東京都", "神奈川県", "長野県", "山梨県", "岐阜県", "静岡県", "愛知県", "三重県", "滋賀県", "京都府", "兵庫県", "大阪府", "奈良県", "和歌山県", "島根県", "岡山県", "鳥取県", "広島県", "山口県", "香川県", "徳島県", "愛媛県", "高知県", "福岡県", "大分県", "佐賀県", "長崎県", "宮崎県", "熊本県", "鹿児島県", "沖縄県"}

–ソート対象データ
set aList to {{"東京都", 301}, {"神奈川県", 98}, {"大阪府", 90}, {"千葉県", 60}, {"埼玉県", 55}, {"北海道", 40}, {"愛知県", 40}, {"京都府", 40}, {"兵庫県", 40}, {"福岡県", 30}, {"静岡県", 20}, {"広島県", 20}, {"茨城県", 20}, {"宮城県", 16}, {"長野県", 15}, {"福島県", 12}, {"新潟県", 11}, {"滋賀県", 10}, {"岐阜県", 10}, {"群馬県", 10}, {"石川県", 10}, {"熊本県", 9}, {"岡山県", 9}, {"福井県", 7}, {"青森県", 7}, {"秋田県", 6}, {"愛媛県", 6}, {"和歌山県", 6}, {"奈良県", 5}, {"三重県", 5}, {"沖縄県", 5}, {"岩手県", 5}, {"大分県", 4}, {"鹿児島県", 4}, {"宮崎県", 4}, {"栃木県", 3}, {"香川県", 3}, {"山形県", 3}, {"富山県", 2}, {"山梨県", 2}, {"長崎県", 2}, {"徳島県", 2}, {"島根県", 2}, {"鳥取県", 1}, {"高知県", 1}, {"山口県", 1}}

set bList to sortItemByIndexList(aList, indexList, 1, 2) of me
–> {{"北海道", 40}, {"青森県", 7}, {"岩手県", 5}, {"秋田県", 6}, {"宮城県", 16}, {"山形県", 3}, {"福島県", 12}, {"新潟県", 11}, {"富山県", 2}, {"石川県", 10}, {"福井県", 7}, {"茨城県", 20}, {"栃木県", 3}, {"群馬県", 10}, {"埼玉県", 55}, {"千葉県", 60}, {"東京都", 301}, {"神奈川県", 98}, {"長野県", 15}, {"山梨県", 2}, {"岐阜県", 10}, {"静岡県", 20}, {"愛知県", 40}, {"三重県", 5}, {"滋賀県", 10}, {"京都府", 40}, {"兵庫県", 40}, {"大阪府", 90}, {"奈良県", 5}, {"和歌山県", 6}, {"島根県", 2}, {"岡山県", 9}, {"鳥取県", 1}, {"広島県", 20}, {"山口県", 1}, {"香川県", 3}, {"徳島県", 2}, {"愛媛県", 6}, {"高知県", 1}, {"福岡県", 30}, {"大分県", 4}, {"佐賀県", 0}, {"長崎県", 2}, {"宮崎県", 4}, {"熊本県", 9}, {"鹿児島県", 4}, {"沖縄県", 5}}

–並び順をインデックスリストで指定してソート
–パラメータ: (1) aList=ソート元リスト ({{"name1", number1}, {"name2", number2}……} )、
–      (2) indexList=ソート順指定用インデックスリスト、
–      (3) itemNo=並べ替え対象アイテム番号(ソート元リスト内の各リスト要素内のソートキーになるアイテムの番号)
–      (4) nohitItem=インデックス項目がソート元リストに対してヒットしなかった場合の、件数0を入れるアイテム番号
–エラー:    エラー時にはfalseを返す
on sortItemByIndexList(aList, indexList, itemNo, nohitItem)
  –処理高速化のためにハンドラ内部にScript Objectを宣言してデータアクセス
  
script sortItemByIndexListObj
    property indexListSpd : indexList
    
property aListSpd : aList
  end script
  
  
–パラメータのエラーチェック
  
set tmpItem to contents of first item of aList
  
set tmpLen to length of tmpItem
  
  
–itemNoのエラーチェック
  
if (itemNo < 1) or (itemNo > tmpLen) then return false
  
–nohitItemのエラーチェック
  
if (nohitItem < 1) or (nohitItem > tmpLen) then return false
  
–itemNoとnohitItemが衝突していないかチェック
  
if itemNo = nohitItem then return false
  
  
  
–処理本体
  
set outList to {}
  
repeat with i in indexListSpd of sortItemByIndexListObj
    set j to contents of i
    
    
set hitF to false
    
    
repeat with ii in aListSpd of sortItemByIndexListObj
      set targItem to contents of item itemNo of ii
      
if targItem = j then
        set the end of outList to contents of ii
        
set hitF to true
        
exit repeat
      end if
    end repeat
    
    
–合致するデータがなかった場合の対応
    
if hitF = false then
      set tmpList to {}
      
repeat nohitItem times
        set the end of tmpList to ""
      end repeat
      
set item itemNo of tmpList to j
      
set item nohitItem of tmpList to 0
      
set the end of outList to tmpList
    end if
    
  end repeat
  
  
outList
  
end sortItemByIndexList

★Click Here to Open This Script 

2013/03/14 入れ子のリストの昇順、降順ソート(超高速版)

入れ子のリスト(2次元配列)を超高速でソート(shell sort)するAppleScriptです。

以前に投稿していただいたScriptを元に、2次元配列にも対応できるよう書き換えたものです。

Core i7 2.66GHzのMacBook Pro Retina mid 2012(OS X 10.8.2)上で、1万レコードの昇順/降順ソートに1秒程度。1.6GHzのCore i5搭載のMacBook Air mid 2011で2秒程度。PowerMac G5 2.7GHz Dual Core(Mac OS X 10.4.11)上で3秒程度です。

上記MacBook Pro上で3万レコードの入れ子のリスト(2次元配列)ソートに5秒程度に対して、入れ子でないリスト(1次元配列)のソート結果が4秒程度。次元が1次元増えてこの程度の変化であれば、なかなかいい結果が出ていると思います。

スクリプト名:入れ子のリストの昇順、降順ソート
–check code 3

script spd
  property aList : {}
  
property bList : {}
end script

–Initialize
set aList of spd to {}
set bList of spd to {}

repeat 10000 times
  set the end of aList of spd to {“aaa”, random number from 1000 to 9999}
end repeat

set sTime to current date

set bList of spd to shellSortListDescending(aList of spd, 2) of me

set eTime to current date

display dialog (eTime - sTime) as string

(*
–check code 1
set aList to {{”abc”, 9}, {”bcd”, 7}, {”ccc”, 3}, {”avc”, 4}, {”dcc”, 5}}
set bList to shellSortListDescending(aList, 2) of me
–> {{”abc”, 9}, {”bcd”, 7}, {”dcc”, 5}, {”avc”, 4}, {”ccc”, 3}}

–check code 2
set cList to shellSortListAscending(aList, 2) of me
–> {{”ccc”, 3}, {”avc”, 4}, {”dcc”, 5}, {”bcd”, 7}, {”abc”, 9}}
*)

–シェルソートで入れ子のリストを昇順ソート
on shellSortListAscending(aSortList, aKeyItem)
  script oBj
    property list : aSortList
  end script
  
set len to count oBj’s list’s items
  
set gap to 1
  
repeat while (gap len)
    set gap to ((gap * 3) + 1)
  end repeat
  
repeat while (gap > 0)
    set gap to (gap div 3)
    
if (gap < len) then
      repeat with i from gap to (len - 1)
        set temp to oBj’s list’s item (i + 1)
        
set j to i
        
repeat while ((j gap) and (contents of item aKeyItem of (oBj’s list’s item (j - gap + 1)) > item aKeyItem of temp))
          set oBj’s list’s item (j + 1) to oBj’s list’s item (j - gap + 1)
          
set j to j - gap
        end repeat
        
set oBj’s list’s item (j + 1) to temp
      end repeat
    end if
  end repeat
  
return oBj’s list
end shellSortListAscending

–シェルソートで入れ子のリストを降順ソート
on shellSortListDescending(aSortList, aKeyItem)
  script oBj
    property list : aSortList
  end script
  
set len to count oBj’s list’s items
  
set gap to 1
  
repeat while (gap len)
    set gap to ((gap * 3) + 1)
  end repeat
  
repeat while (gap > 0)
    set gap to (gap div 3)
    
if (gap < len) then
      repeat with i from gap to (len - 1)
        set temp to oBj’s list’s item (i + 1)
        
set j to i
        
repeat while ((j gap) and (contents of item aKeyItem of (oBj’s list’s item (j - gap + 1)) < item aKeyItem of temp))
          set oBj’s list’s item (j + 1) to oBj’s list’s item (j - gap + 1)
          
set j to j - gap
        end repeat
        
set oBj’s list’s item (j + 1) to temp
      end repeat
    end if
  end repeat
  
return oBj’s list
end shellSortListDescending

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

2013/03/13 入れ子ではないリストの昇順、降順ソート(超高速版)

入れ子ではないただのリスト(1次元配列)を、昇順・降順ソートを超高速に行うAppleScriptのサブルーチンです。

前に投稿していただいた高速ソートルーチンを、サンプルのまま整備していなかったので、降順のものを作っておきました。

あとは、日常的には入れ子のリスト(2次元配列)をソートするものが絶対に不可欠なので、このあたりを整備しておく必要があります。

ソートルーチンやリストの各種操作ルーチンを整備しまくっておいたおかげで、よほどのことがなければFileMaker Proなどのデータベースを併用する必要は感じません。

(AppleScriptを覚えて)最初のうちは、FileMaker ProやJedit上の機能におんぶにだっこのScriptを組む気楽さから、とてもこれらのアプリケーションなしで過ごすことはできなかったものですが(とくにソート)……逆にいえば、自分の作るプログラムがJeditやFileMaker Proなしの環境では動かないケースが多くなってしまうわけで、脱FileMaker Proのためにルーチンを整備しはじめたという側面もあります。

スクリプト名:入れ子ではないリストの昇順、降順ソート
set aList to {1, 2, 3, 4, 5}

set bList to shellSortDescending(aList) of me
–> {5, 4, 3, 2, 1}

set cList to shellSortAscending(aList) of me
–> {1, 2, 3, 4, 5}

–入れ子ではないリストの昇順ソート
on shellSortAscending(aSortList)
  script oBj
    property list : aSortList
  end script
  
set len to count oBj’s list’s items
  
set gap to 1
  
repeat while (gap len)
    set gap to ((gap * 3) + 1)
  end repeat
  
repeat while (gap > 0)
    set gap to (gap div 3)
    
if (gap < len) then
      repeat with i from gap to (len - 1)
        set temp to oBj’s list’s item (i + 1)
        
set j to i
        
repeat while ((j gap) and (oBj’s list’s item (j - gap + 1) > temp))
          set oBj’s list’s item (j + 1) to oBj’s list’s item (j - gap + 1)
          
set j to j - gap
        end repeat
        
set oBj’s list’s item (j + 1) to temp
      end repeat
    end if
  end repeat
  
return oBj’s list
end shellSortAscending

–入れ子ではないリストの降順ソート
on shellSortDescending(aSortList)
  script oBj
    property list : aSortList
  end script
  
set len to count oBj’s list’s items
  
set gap to 1
  
repeat while (gap len)
    set gap to ((gap * 3) + 1)
  end repeat
  
repeat while (gap > 0)
    set gap to (gap div 3)
    
if (gap < len) then
      repeat with i from gap to (len - 1)
        set temp to oBj’s list’s item (i + 1)
        
set j to i
        
repeat while ((j gap) and (oBj’s list’s item (j - gap + 1) < temp))
          set oBj’s list’s item (j + 1) to oBj’s list’s item (j - gap + 1)
          
set j to j - gap
        end repeat
        
set oBj’s list’s item (j + 1) to temp
      end repeat
    end if
  end repeat
  
return oBj’s list
end shellSortDescending

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

2012/03/10 シェルソート高速化比較(さらに改良)

先日投稿していただいたshell sortに、若干の改良を加えてさらに高速化したAppleScriptです。

tsuki3さんから投稿していただいたshell sortのコメントに、

ちなみに私の経験上、リストの要素数を得る場合は “lenth of ~” を使うよりも
“count ~” を用いた方が速いはずです。また、私のスクリプトでは 複数の要素を纏めて set しておりますが、
これを別個にすればより速くなるはずです。

とあったので、すべて試してみました。

sort2.jpg

MacBook Pro 2010 Core i7 2.66GHz, RAM 8GB, Mac OS X 10.6.8の環境で改良版の「shellSort5」を試してみたところ、shellSort4で5秒かかっていた3万件のソートが4秒に。正直、こんなに速くなるとは思ってもみませんでした。ループ回数が多いので、わずかなスピードアップの集積が大きいんでしょうね。

Classic Mac OSの時代には、サードパーティのOSAX(スクリプティング機能拡張)の機能を呼び出してハードウェア制御(キーボードLEDの点灯制御、シリアルポート経由の機器制御)を行ったり、高度な命令を利用することを目指していました。最終的には200個ぐらいのOSAXをシステムにインストールして使っていたほど。

ソートに関しても、サードパーティのOSAXで提供されるソート命令を利用することが多かったのですが、Mac OS Xの時代になってMac OS X対応できない(or しない)OSAXが多数あったため、OSAXへの依存度を下げることが必要になってきました。のちに、Universal Binary対応や64ビット対応など、さまざまなハードルがあってOSAX側の対応が後手後手に回る中、Mac OS Xへの移行当初からOSAXへの依存度を下げる努力をしてきたため、自分はあまり影響を受けませんでした。また、このOSAX依存度軽減の努力は、AppleScript StudioによるGUIベースのアプリケーション開発時にもおおいに役立ちました(アプリケーションと一緒に再配布できないライセンス条件のOSAXなどもあったため)。

正直なところ、ソートルーチンなんて一度作ってそこそこ満足できれば、見直すことなどほとんどない部品。最初のバージョンからここまでいろいろと高速化ができたのは、多くのScripterの方々の努力と経験によるところが大きく、1人でやっているだけでは、なかなか手が回らないところだと思います。

そういう意味では、こういうBlogを立ち上げて地道に活動してきた意義というのはあるのではないかと。この3月でスタートして4年が経過。掲載Scriptも1,000本を超えました。当初の、「サブルーチン資産に対するMac OS Xバージョンアップの影響検証」というねらいを超えて、技術的な交流の場として機能していけたらいいですね。

スクリプト名:(数値ソート) シェルソート_v2.scpt
set lst to {}
set lstr to (a reference to lst)
repeat 30000 times
  set lstr’s end to (random number 999)
end repeat
set t to (current date)

set res to my shellSort5(lst)

– 正誤確認用
(*set resr to (a reference to res)
repeat with i from 2 to (count resr)
  if (resr’s item (i - 1) > resr’s item i) then return false
end repeat*)

return {“” & ((current date) - t) & “秒”}

on shellSort1(lst) – 配列を使用した基本形
  set len to (count lst)
  
set gap to 1
  
repeat while (gap len)
    set gap to ((gap * 3) + 1)
  end repeat
  
repeat while (gap > 0)
    set gap to (gap div 3)
    
if (gap < len) then
      repeat with i from gap to (len - 1)
        set {temp, j} to {lst’s item (i + 1), i}
        
repeat while ((j gap) and (lst’s item (j - gap + 1) > temp))
          set {lst’s item (j + 1), j} to {lst’s item (j - gap + 1), j - gap}
        end repeat
        
set lst’s item (j + 1) to temp
      end repeat
    end if
  end repeat
  
return lst
end shellSort1

on shellSort2(lst) – 参照 (a reference to…) を使用
  set {lstr, len, gap} to {a reference to lst, count lst, 1}
  
repeat while (gap len)
    set gap to ((gap * 3) + 1)
  end repeat
  
repeat while (gap > 0)
    set gap to (gap div 3)
    
if (gap < len) then
      repeat with i from gap to (len - 1)
        set {temp, j} to {lstr’s item (i + 1), i}
        
repeat while ((j gap) and (lstr’s item (j - gap + 1) > temp))
          set {lstr’s item (j + 1), j} to {lstr’s item (j - gap + 1), j - gap}
        end repeat
        
set lstr’s item (j + 1) to temp
      end repeat
    end if
  end repeat
  
return lst
end shellSort2

on shellSort3(lst) – レコードと参照、グローバル変数を併用
  global rec
  
set rec to {list:lst}
  
set {recr, len, gap} to {a reference to rec, count lst, 1}
  
repeat while (gap len)
    set gap to ((gap * 3) + 1)
  end repeat
  
repeat while (gap > 0)
    set gap to (gap div 3)
    
if (gap < len) then
      repeat with i from gap to (len - 1)
        set {temp, j} to {recr’s list’s item (i + 1), i}
        
repeat while ((j gap) and (recr’s list’s item (j - gap + 1) > temp))
          set {recr’s list’s item (j + 1), j} to {recr’s list’s item (j - gap + 1), j - gap}
        end repeat
        
set recr’s list’s item (j + 1) to temp
      end repeat
    end if
  end repeat
  
return recr’s list
end shellSort3

on shellSort4(lst) – スクリプトオブジェクトを使用
  script o
    property list : lst
  end script
  
set {len, gap} to {count o’s list’s items, 1}
  
repeat while (gap len)
    set gap to ((gap * 3) + 1)
  end repeat
  
repeat while (gap > 0)
    set gap to (gap div 3)
    
if (gap < len) then
      repeat with i from gap to (len - 1)
        set {temp, j} to {o’s list’s item (i + 1), i}
        
repeat while ((j gap) and (o’s list’s item (j - gap + 1) > temp))
          set {o’s list’s item (j + 1), j} to {o’s list’s item (j - gap + 1), j - gap}
        end repeat
        
set o’s list’s item (j + 1) to temp
      end repeat
    end if
  end repeat
  
return o’s list
end shellSort4

–プレーンなAppleScriptベースでは最速ソート
on shellSort5(aSortList) – スクリプトオブジェクトを使用
  script oBj
    property list : aSortList
  end script
  
set len to count oBj’s list’s items
  
set gap to 1
  
repeat while (gap len)
    set gap to ((gap * 3) + 1)
  end repeat
  
repeat while (gap > 0)
    set gap to (gap div 3)
    
if (gap < len) then
      repeat with i from gap to (len - 1)
        set temp to oBj’s list’s item (i + 1)
        
set j to i
        
repeat while ((j gap) and (oBj’s list’s item (j - gap + 1) > temp))
          set oBj’s list’s item (j + 1) to oBj’s list’s item (j - gap + 1)
          
set j to j - gap
        end repeat
        
set oBj’s list’s item (j + 1) to temp
      end repeat
    end if
  end repeat
  
return oBj’s list
end shellSort5

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

2012/03/07 シェルソート高速化比較

AppleScriptにおけるシェルソートの高速化について、それぞれ検証するScriptをtsuki3さんから投稿していただきました。

3/3 の記事 “100倍速いシェルソートでリストをソート” 内、解説の項にて
高速化の計測を話題にされておりましたが、数年前に私が書いたスクリプトが
それを目的としたものでしたので、投稿いたします。

なお、拙作のスプリクトオブジェクトを用いたものの方が誤差程度ですが、
edama2さんのものより(当方の環境では)速いようです。
ただ、レギュレーションが違うので比較自体に意味が無いのかもしれませんが。

ちなみに私の経験上、リストの要素数を得る場合は “lenth of ~” を使うよりも
“count ~” を用いた方が速いはずです。また、私のスクリプトでは 複数の要素を纏めて set しておりますが、
これを別個にすればより速くなるはずです。

一度、きちんと比較しておかないと……とは思ってはいたのですが、ついついめんどくさくて手つかずの状態。ちょうどいいところに投稿していただけたので、実測値を計ってみることにしました。

計測するのは、以下の4つ。

 shell sort 1……高速化を一切行っていない、通常のシェルソート
 shell sort 2……オーソドックスな「a reference to」を使用して高速化
 shell sort 3……レコードと参照、グローバル変数を併用
 shell sort 4……スクリプトオブジェクトを使用

……shell sort 3は、ちょっとお目にかかったことのないタイプ。

ひととおり、MacBook Pro 2010 Core i7 2.66GHz, RAM 8GB, Mac OS X 10.6.8の環境でテストしてみると……

sort.jpg

スクリプトオブジェクトを使用するものが一番高速(AppleScriptObjCを使うと、そちらの方が速いですが)ということが客観的によく分りました。

スクリプト名:(数値ソート) シェルソート.scpt
set lst to {}
set lstr to (a reference to lst)
repeat 30000 times
  set lstr’s end to (random number 999)
end repeat
set t to (current date)

set res to my shellSort4(lst)

– 正誤確認用
(*set resr to (a reference to res)
repeat with i from 2 to (count resr)
  if (resr’s item (i - 1) > resr’s item i) then return false
end repeat*)

return {“” & ((current date) - t) & “秒”}

on shellSort1(lst) – 配列を使用した基本形
  set len to (count lst)
  
set gap to 1
  
repeat while (gap len)
    set gap to ((gap * 3) + 1)
  end repeat
  
repeat while (gap > 0)
    set gap to (gap div 3)
    
if (gap < len) then
      repeat with i from gap to (len - 1)
        set {temp, j} to {lst’s item (i + 1), i}
        
repeat while ((j gap) and (lst’s item (j - gap + 1) > temp))
          set {lst’s item (j + 1), j} to {lst’s item (j - gap + 1), j - gap}
        end repeat
        
set lst’s item (j + 1) to temp
      end repeat
    end if
  end repeat
  
return lst
end shellSort1

on shellSort2(lst) – 参照 (a reference to…) を使用
  set {lstr, len, gap} to {a reference to lst, count lst, 1}
  
repeat while (gap len)
    set gap to ((gap * 3) + 1)
  end repeat
  
repeat while (gap > 0)
    set gap to (gap div 3)
    
if (gap < len) then
      repeat with i from gap to (len - 1)
        set {temp, j} to {lstr’s item (i + 1), i}
        
repeat while ((j gap) and (lstr’s item (j - gap + 1) > temp))
          set {lstr’s item (j + 1), j} to {lstr’s item (j - gap + 1), j - gap}
        end repeat
        
set lstr’s item (j + 1) to temp
      end repeat
    end if
  end repeat
  
return lst
end shellSort2

on shellSort3(lst) – レコードと参照、グローバル変数を併用
  global rec
  
set rec to {list:lst}
  
set {recr, len, gap} to {a reference to rec, count lst, 1}
  
repeat while (gap len)
    set gap to ((gap * 3) + 1)
  end repeat
  
repeat while (gap > 0)
    set gap to (gap div 3)
    
if (gap < len) then
      repeat with i from gap to (len - 1)
        set {temp, j} to {recr’s list’s item (i + 1), i}
        
repeat while ((j gap) and (recr’s list’s item (j - gap + 1) > temp))
          set {recr’s list’s item (j + 1), j} to {recr’s list’s item (j - gap + 1), j - gap}
        end repeat
        
set recr’s list’s item (j + 1) to temp
      end repeat
    end if
  end repeat
  
return recr’s list
end shellSort3

on shellSort4(lst) – スクリプトオブジェクトを使用
  script o
    property list : lst
  end script
  
set {len, gap} to {count o’s list’s items, 1}
  
repeat while (gap len)
    set gap to ((gap * 3) + 1)
  end repeat
  
repeat while (gap > 0)
    set gap to (gap div 3)
    
if (gap < len) then
      repeat with i from gap to (len - 1)
        set {temp, j} to {o’s list’s item (i + 1), i}
        
repeat while ((j gap) and (o’s list’s item (j - gap + 1) > temp))
          set {o’s list’s item (j + 1), j} to {o’s list’s item (j - gap + 1), j - gap}
        end repeat
        
set o’s list’s item (j + 1) to temp
      end repeat
    end if
  end repeat
  
return o’s list
end shellSort4

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

2012/03/03 100倍速いシェルソートでリストをソート

edama2さんよりの投稿です

基本的には掲載されている「シェルソートでリストをソート」をスプリクトオブジェクトで処理にしているだけです。サンプルでソートする数が100倍なだけで「100倍速い」という数字には特に根拠はありません。

スクリプト名:100倍速いシェルソートでリストをソート
set theList to {}
set i to 1
repeat 30000 times
  copy (random number from 100 to 99999) to the end of theList
  
set i to i + 1
end repeat

set s0Time to current date
set aRes to shellSort_order_(theList, false) of me
set s1Time to current date

display dialog (s1Time - s0Time) as string

–シェルソートでリストを昇順ソート(入れ子ではないリスト)
on shellSort_order_(a, isAsc)
  
  
script a_ref
    property contents : a
  end script
  
  
set n to length of a
  
set cols to {1391376, 463792, 198768, 86961, 33936, 13776, 4592, 1968, 861, 336, 112, 48, 21, 7, 3, 1}
  
repeat with h in cols
    if (h (n - 1)) then
      repeat with i from h to (n - 1)
        set v to item (i + 1) of contents of a_ref
        
set j to i
        
repeat while (j h) and ((item (j - h + 1) of contents of a_ref) > v)
          set (item (j + 1) of contents of a_ref) to (item (j - h + 1) of contents of a_ref)
          
set j to j - h
        end repeat
        
set item (j + 1) of contents of a_ref to v
      end repeat
    end if
  end repeat
  
  
if not isAsc then set a to a’s reverse
  
  
return a
end shellSort_order_

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

Piyomaru Softwareによる解説:

以前に掲載したシェルソートのルーチンの改良版です。ソートというか、巨大なリスト型変数(配列)の操作をAppleScriptで行うとスピードが低下するので、3,000〜4,000要素(Item)を超えるサイズのリスト型変数を扱う場合には、何らかの高速化を行うべきです。

AppleScriptにおけるソートの高速化手法にはおよそ3つあって、1つが「a reference to」による間接アクセスによる高速化。

著しく高速化するものの、データを格納する変数と間接アクセスする変数の2つをグローバル宣言する必要があり、効果は大きいものの、副作用も大きい(グローバル宣言した変数が他のルーチンの変数をぶつかっていないか確認が必要、など)ため、そのための作業コストが大きく「手軽」とはいえませんでした。

2つ目が、ここで活用されている「スクリプトオブジェクト内にプロパティを持つ」というアクセス方法で、a reference toと同程度ぐらいには(厳密に計測してはいないのですが)高速化することが知られています。

スクリプトオブジェクトの利用による高速化処理の前後で、サンプルの3万項目の乱数リストをソート所要時間を計測比較してみたところ(MacBook Pro Core i7 2.66GHz, Mac OS X 10.6.8, 8GB RAM)、

 高速化前:2967秒
 高速化後:8秒

と、370倍ぐらい高速になっていました。リストの項目が大きいほど高速化の効果が出やすいものと思われます。

3つめが、Mac OS X 10.6から使えるようになったAppleScript ObjCを用いること。この環境内でCocoaのソートルーチンを呼び出すと速いので、一度すべてのやりかたでどのソート方法が速いのか、比較をしておくべきでしょう。

追記(2012/3/4):

MacBook Air 2011 (Core i5 1.6GHz、Mac OS X 10.7.3、RAM 4GB)でこの乱数3万件のソートを試してみたところ……

 本ScriptをAppleScript Editor上で実行:7秒
 本ScriptをAppleScriptObjC on Xcodeのプロジェクトで実行:8秒
 AppleScriptObjC on XcodeのCocoaベースのソートでリスト(正確にはレコード)をScriptのオブジェクトに持たせた場合:129秒
 AppleScriptObjC on XcodeのCocoaベースのソートで何も高速化処理を行っていない場合:(計測不能)

Cocoaベースのソートがたいして速くない(純粋にリスト=NSArrayではないので、同じ処理ではないですが)ことに驚きました。AppleScriptObjCのプログラムも、ソート処理をAppleScriptネイティブのものに書き換えておいたほうがよさそうな……。

あるいは、3万件というデータ件数になると、AppleScriptObjCでもCoreDataを併用することを検討すべきなのかもしれません。データベースに入れてしまえば、3万件ごときのソートなど一瞬でしょうし。

さらに追記(2012/3/4):

AppleScriptObjCで以下のようなプログラム(単なるArrayをソート)にしてみたところ、3万件でもソートに要する時間は1秒以下でした(MacBook Pro Core i7 2.66GHz, Mac OS X 10.6.8, RAM 8GB)。

AppleScriptObjCファイル名:sortArrayAppDelegate.applescript

– sortArrayAppDelegate.applescript
– sortArray

– Created by Takaaki Naganoya on 12/03/04.
– Copyright 2012 Piyomaru Software. All rights reserved.

script aRef
  
property contents : {}
end script

script sortArrayAppDelegate
  
property parent : class “NSObject”
  
  
on applicationWillFinishLaunching_(aNotification)
    
– Nothing
  
end applicationWillFinishLaunching_
  
  
on applicationShouldTerminate_(sender)
    
return current application’s NSTerminateNow
  
end applicationShouldTerminate_
  
  
on clicked_(sender)
    
    
set aList to {}
    
repeat 30000 times
      
tell current application
        
set the end of (contents of aRef) to (random number from 1000 to 9999) as string
      
end tell
    
end repeat
    
    
tell current application
      
set sTime to current date
    
end tell
    
    
set theArray to current application’s class “NSMutableArray”’s arrayWithArray_(contents of aRef)
    
set bList to theArray’s sortedArrayUsingSelector_(“localizedCaseInsensitiveCompare:”)
    
    
tell current application
      
set eTime to current date
      
set totalTime to eTime - sTime
    
end tell
    
    
display dialog (totalTime as string)
    
  
end clicked_
  
end script

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

2011/01/14 シェルソートでリストをソート

フラットな(入れ子ではない)リストをシェルソートでソートするAppleScriptです。冒頭にあるのはテスト用データ作成部分です。

入れ子になっているリスト、{{1,2,3,4}, {2,3,4,5}, {3,4,5,6}} をソートする機会はめちゃめちゃ多いのですが、単なるフラットなリストだと使用頻度はぐっと低く、わざわざ昇順/降順のシェルソートルーチンを用意していませんでした(もっと遅い部品を使っていました)。

たまたま、スピードにこだわりたい処理があったのでシェルソートで単純なリストの昇順/降順ソートをまとめておきました。

スクリプト名:シェルソートでリストをソート

set theList to {}
set i to 1
repeat 300 times
  copy (random number from 100 to 999) to the end of theList
  
set i to i + 1
end repeat

set s0Time to current date
set aRes to shellSortDecending(theList) of me
set s1Time to current date

display dialog (s1Time - s0Time) as string
aRes

–シェルソートでリストを昇順ソート(入れ子ではないリスト)
on shellSortAscending(a)
  set n to length of a
  
set cols to {1391376, 463792, 198768, 86961, 33936, 13776, 4592, 1968, 861, 336, 112, 48, 21, 7, 3, 1}
  
repeat with h in cols
    if (h (n - 1)) then
      repeat with i from h to (n - 1)
        set v to item (i + 1) of a
        
set j to i
        
repeat while (j h) and ((contents of item (j - h + 1) of a) > v)
          set (item (j + 1) of a) to (item (j - h + 1) of a)
          
set j to j - h
        end repeat
        
set item (j + 1) of a to v
      end repeat
    end if
  end repeat
  
return a
end shellSortAscending

–シェルソートでリストを降順ソート(入れ子ではないリスト)
on shellSortDecending(a)
  set n to length of a
  
set cols to {1391376, 463792, 198768, 86961, 33936, 13776, 4592, 1968, 861, 336, 112, 48, 21, 7, 3, 1}
  
repeat with h in cols
    if (h (n - 1)) then
      repeat with i from h to (n - 1)
        set v to item (i + 1) of a
        
set j to i
        
repeat while (j h) and ((contents of item (j - h + 1) of a) < v)
          set (item (j + 1) of a) to (item (j - h + 1) of a)
          
set j to j - h
        end repeat
        
set item (j + 1) of a to v
      end repeat
    end if
  end repeat
  
return a
end shellSortDecending

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

2008/07/26 shell sortで入れ子のリストをソート

shell sortのルーチンをいじくり回して、入れ子になっているリスト(例:{{1,”abc”},{2,”cde”}})をソートできるようにしてみました。Bubble SortやMax Sortの5倍、いつも使っているApple製ソートルーチン改の2.5倍高速です。
(more…)

2008/07/25 シェルソート

シェルソートでリストをソートするルーチンです。やっぱり速い(^ー^;; これを入れ子のリストに対応させたら素敵。
(more…)

2008/07/25 挿入ソートを入れ子のリストに対して行う(ソートキー項目指定つき)

挿入ソート(Insertion Sort)のルーチンに手を加えて、昇順/降順のルーチンを別々に用意し、入れ子になったリストをソートできるように書き換え、各リスト項目のどのアイテムをソート用のキーにするか指定できるようになっています。
(more…)

2008/07/25 Insertion Sortでリストをソートする

Insertion Sortでリストをソートします。前出のBubble Sort、Max Sortよりも高速です。300件の乱数リストを用いたところ、Max Sortが10秒、Bubble Sortが9秒、Insertion Sortが7秒という結果が出ました。
(more…)