swiftで最小限タートルグラフィック
swift playgrouds での4番目のプログラミング。今回はタートルグラフィックスの実装。
機能は最小限の LOGO の機能で有名なタートルグラフィックスをswiftで実装した。
実装したコマンドは、以下の6つ。カラーは未実装。
- right
- left
- forward
- backward
- penDown
- penUp
これらのコマンドの組み合わせで、以下のようなグラフィックを表現できる。





作ったクラスとその概要
ユーティリティー的なコードを除外すると、以下のような単純なコードです。
struct tinyTurtle
{
let path = NSBezierPath()
var position = CGPoint(x:0, y:0) // 絶対値での現在地
var degree = CGFloat(90.0) // 角度。水平方向の右側が0度なので最初は上を向ける
var penState = true // ペンを置いているか否か
init()
{
self.path.move(to: position)
}
// 現在の方向から計回りに角度を曲げる
mutating func right(_ degree: CGFloat)
{
self.degree -= degree
}
// 逆回転も追加
mutating func left(_ degree: CGFloat)
{
right(-degree)
}
// 現在の位置から前進
mutating func forward(_ length: CGFloat)
{
// 角度と長さと現在地から、前進後の位置を求める
let radian = self.degree * (CGFloat.pi / 180) //ラジアンに変換
let vector = length * CGPoint(x: cos(radian), y: sin(radian))
let absPoint = self.path.currentPoint + vector
if penState
{
self.path.line(to: absPoint)
}
else
{
self.path.move(to: absPoint)
}
}
// バックも追加
mutating func backward(_ length: CGFloat)
{
forward(-length)
}
// 描画用のペンの上げ下げ
mutating func penDown()
{
self.penState = true
}
mutating func penUp()
{
self.penState = false
}
// 絶対値で示す位置へ移動 描画ポイントが迷子になっちゃうので追加
mutating func goto(_ x: CGFloat, _ y: CGFloat)
{
self.position = CGPoint(x:x, y:y)
self.path.move(to: self.position)
}
func draw()
{
self.path.stroke()
}
}
解説は、 関東swift勉強会2017-6 で行います。
使い方
多角形を描く。
NSImage.imageWithBlock(NSSize(width:300, height:200))
{
var t = tinyTurtle()
t.goto(150,100)
func poly(_ n: Int, _ size: CGFloat)
{
for _ in 1...n
{
t.forward(size)
t.right(360.0 / CGFloat(n))
}
}
poly(3, 50)
poly(5, 50)
poly(8, 50)
t.draw()
}
出力結果は、以下のようになる。

ユーティリテーメソッドを利用すると、以下のようにもかける。
tinyTurtle.tinyTurtleWithBlock(NSSize(width:300, height:200))
{
t in
func poly(_ n: Int, _ size: CGFloat)
{
for _ in 1...n
{
t.forward(size)
t.right(360.0 / CGFloat(n))
}
}
poly(3, 50)
poly(5, 50)
poly(8, 50)
}
他にも、コッホ曲線を描く場合はこんな感じになります。
tinyTurtle.tinyTurtleWithBlock(NSSize(width:300, height:200))
{
t in
func koch(_ size: CGFloat, _ angle:CGFloat, _ n:Int)
{
if n == 0
{
t.forward(size)
return
}
else
{
koch(size/3, angle, n-1)
t.left(angle)
koch(size/3, angle, n-1)
t.right(angle * 2)
koch(size/3, angle, n-1)
t.left(angle)
koch(size/3, angle, n-1)
}
}
t.goto(50,20)
t.right(90)
koch(200, 60, 5)
}
結果は、以下のようになります。

参考文献とソースコード
- Logo 人工知能へのアプローチ
- https://www.amazon.co.jp/Logo-人工知能へのアプローチ-ラジオ技術選書-150-祐安-重夫/dp/4844301500
swiftで三段論法
swift playgrouds での3番目のプログラミング。今回は、三段論法を使用する人工知能っぽい何か。

機械に推論させるのは、難しいと思われるかもしれない。三段論法くらいであれば、結構単純なアルゴリズムで推論が実行できる。
大雑把な解説
プログラムは、structが一つ、メソッドは6つ、プロパティが1つ、の単純な構成です。
大きく分けて、2つの部分からなっています。
- 三段論法を実行する部分
- プロパティ: “知識集” 三段論法で使用する事実を保持します
- メソッド : “デバッグ表示” デバッグ用に “知識集” の内容を出力します。
- メソッド : “登録” 知識の登録をします。
- メソッド : “検索” 引数で与えられた単語の知識を検索します。
- メソッド : “推論” 引数2つで与えられた知識の真偽を推論します。
- 自然言語の解析部分
- メソッド : “分ち書き” 文字列を単語に分けます。
- メソッド : “入力” 与えられた文字列の内容に従って、知識の登録、知識の真偽を判定します。
データ構造も非常にシンプルです。
var 知識集: [(lhs: String, rhs: String)] = []
タプルの配列です。「lhsはrhsである」との形式で知識を溜め込みます。
例えば、「ソクラテスは人間です」は、タプルとして
(lhs: "ソクラテス", rhs: "人間")
とタプルの形式にして、プロパティ “知識集” に蓄積されてゆきます。
参考文献とソースコード
- Logo 人工知能へのアプローチ
- https://www.amazon.co.jp/Logo-人工知能へのアプローチ-ラジオ技術選書-150-祐安-重夫/dp/4844301500
swiftでハノイの塔
swift playgrouds で2番目に書いたプログラム。
機能はパズル ハノイの塔 の手順を出力するだけ。
手順は以下のような、アニメーション(wikipediaより引用)ではなくて、単なる文字列です。

作った関数
func hanoi(diskCount n: Int64) -> [(pickUp: String, putDown: String)]
今回のメイン関数。円盤の枚数を与えると、手順をタプルのリストとして返す。 “pickUp” から “putDown” へ円盤を移動する。
func printHanoi01(hanoiArray: [(pickUp: String, putDown: String)])
関数 “hanoi” の表示をログに出力するための関数。
printHanoi01(hanoiArray: hanoi(diskCount:3))
上記のように評価すると、
(pickUp: "left", putDown: "right")
(pickUp: "left", putDown: "center")
(pickUp: "right", putDown: "center")
(pickUp: "left", putDown: "right")
(pickUp: "center", putDown: "left")
(pickUp: "center", putDown: "right")
(pickUp: "left", putDown: "right")
と出力される。
func hanoi(diskCount n: Int64) -> [(pickUp: String, putDown: String)]
こちらは、出力を日本語にして見た。
同じく出力は以下のようになる。
円盤を 左側 から 右側 に移動する
円盤を 左側 から 中央 に移動する
円盤を 右側 から 中央 に移動する
円盤を 左側 から 右側 に移動する
円盤を 中央 から 左側 に移動する
円盤を 中央 から 右側 に移動する
円盤を 左側 から 右側 に移動する
コードを書いての感想
関数hanoiの中で、関数hanoi_internalを定義して使っている。Pascal風にローカル関数が書けるようになったのは便利。
hanoi本体は以下のような感じです。
// お皿の枚数を渡すと、ハノイの塔の手順を示す関数
func hanoi(diskCount n: Int64) -> [(pickUp: String, putDown: String)]
{
// 関数の中で関数定義ができる
func hanoi_internal(diskCount n: Int64, from x:String, to y:String, work z:String) -> [(pickUp: String, putDown: String)]
{
if n != 0
{
return hanoi_internal( diskCount:n - 1, from:x, to:z, work:y)
+ [(pickUp: x, putDown: y)]
+ hanoi_internal( diskCount:n - 1, from:z, to:y, work:x)
}
else
{
return []
}
}
return hanoi_internal(diskCount:n, from:"left", to:"right", work:"center")
}
hanoi_internalを無名関数にしてより簡潔に表記する方法がありそうだが見当たらなかった。
無名関数の中で自分自身へのポインターを示す特殊なキーワードがありそう。引数だと$0, $1, $2…などの表記があるので、$$がや$_あたりにあれば良いなと妄想中。
// こんな表記ができると最高なんだが...... $_を関数自身へのポインターとみなした場合
func hanoi(diskCount n: Int64) -> [(pickUp: String, putDown: String)]
{
return {
diskCount n: Int64, from x:String, to y:String, work z:String)
-> [(pickUp: String, putDown: String)] in
if n != 0
{
return $_( diskCount:n - 1, from:x, to:z, work:y)
+ [(pickUp: x, putDown: y)]
+ $_( diskCount:n - 1, from:z, to:y, work:x)
}
else
{
return []
}
}(diskCount:n, from:"left", to:"right", work:"center")
}
参考文献とソースコード
- Logo 人工知能へのアプローチ
- https://www.amazon.co.jp/Logo-人工知能へのアプローチ-ラジオ技術選書-150-祐安-重夫/dp/4844301500
MacPortsでInstallしたPythonを有効にした時のメモ
解決するのに非常に以外に時間がかかった。
MacPorts側の解決。
sudo port select pip pip34
sudo port select python python34
sudo port select python3 python34
sudo port select sphinx py34-sphinx
ここまでは、install時にコメントが出るので簡単。
さらにPython系のツール群へパスを通すために、bash_profileに以下を追加。
# Python系のツール群へパスを通す
export PATH="/opt/local/Library/Frameworks/Python.framework/Versions/3.4/bin:$PATH"
このパスの位置がMacPortsの流儀のようで探すのに苦労した。
MacPortsでインストールしたツールは、/opt/local/bin/へシンボリックリンクを貼が貼られる。
一方、pipでinstallしたものは、シンボリックリンクは貼られない。pipはMacPortsの事は知らないので当然か。
自分でツール毎にシンボリックリンクを作るか、/opt/local/Library/Frameworks/Python.framework/Versions/3.4/binへパスを通すか迷ったが、パスを通す方を選んだ。