Apple Silicon MacにてFree PascalでGUIアプリを作成する統合開発環境 Lazarusを導入しました。
Lazarusはbrewコマンドでインストールしました。Intel CPU用にビルドしたバイナリのため、Rosetta2が必要になります。
前回紹介したCube Explorerを移植できないか試してみます。
Apple Silicon MacにてFree PascalでGUIアプリを作成する統合開発環境 Lazarusを導入しました。
Lazarusはbrewコマンドでインストールしました。Intel CPU用にビルドしたバイナリのため、Rosetta2が必要になります。
前回紹介したCube Explorerを移植できないか試してみます。
Cube Explorerという素晴らしいアプリを見つけました。
Windows専用なのでApple Silicon MacではWineを使って起動させました。
デフォルトでは上面黄、正面赤になっているため、基本ポジション(上面白・正面緑)にカスタマイズしました。一度設定すると自動的に保存されます。
右下のEnter Meneuverにスクランブル(csTimerで生成)を入力してApplyボタンを押すとキューブを自動操作してくれます。
そしてAdd and Solveボタンを押すと左側のメインウィンドウに解法を表示します。今回のスクランブルは22手で解くことができます。
このアプリのソースはPascalで書かれています(環境はDelphi 6)。Windows11ではビルドできなくなっているようです。時間があれば、Macに移植できないか検討したいところです。
brewのsageプログラムでルービックキューブの展開図を書いてみました。
配色が実際のルービックキューブとは異なるので、展開図ではPythonスクリプトによる変更が必要です。世界標準配色では、白黄、青緑、赤オレンジの対になります。
ただし、plot3d_cubeではスクリプトでの色変更ができません。開発者のDavid Joynerによるとrubik.plot3d_cubeのソースコードを書き換える必要があるそうです。
まあこの方は群論専門の数学者であってキュービストではないので仕方ないです。
ルービックキューブに特化したプログラムをGitHubで見つける方が良さそうです。
from sage.all import *
rubik = CubeGroup()
rubik.plot_cube("")
[Mac M2 Pro 12CPU, Ventura 13.6, clang 15.0.0]
教本:『詳解 Objective-C 2.0 第3版』(2011年) chap.04-01
動的バインディングとポリモーフィズムについて学びました。
コードの簡素化に寄与する反面、実行速度が遅くなるようです。
プログラミングをミスった場合、静的バインディングであればコンパイルエラーになり、動的バインディングであれば実行時エラーになります。
これは実務レベルで体感してみないとメリット・デメリットを深く理解できないように思いました。
取りあえず先に進みます。
#import <Foundation/NSObject.h>
#import <stdio.h>
@interface A : NSObject
- (void)whoAreYou;
@end
@implementation A
- (void)whoAreYou { printf("I'm A\n"); }
@end
@interface B : NSObject
- (void)whoAreYou;
@end
@implementation B
- (void)whoAreYou { printf("I'm B\n"); }
@end
int main(void)
{
id obj;
int n;
scanf("%d", &n);
switch (n) {
case 0: obj = [[A alloc] init]; break;
case 1: obj = [[B alloc] init]; break;
case 2: obj = [[NSObject alloc] init]; break;
}
[obj whoAreYou];
return 0;
}
1
I'm B
2
2023-10-16 10:28:08.551 dyna[12438:464557] -[NSObject whoAreYou]: unrecognized selector sent to instance 0x600002a70000
2023-10-16 10:28:08.552 dyna[12438:464557] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSObject whoAreYou]: unrecognized selector sent to instance 0x600002a70000'
*** First throw call stack:
(
0 CoreFoundation 0x00000001aac0b104 __exceptionPreprocess + 176
1 libobjc.A.dylib 0x00000001aa729fd0 objc_exception_throw + 60
2 CoreFoundation 0x00000001aacb210c -[NSObject(NSObject) __retain_OA] + 0
3 CoreFoundation 0x00000001aab73030 ___forwarding___ + 1600
4 CoreFoundation 0x00000001aab72930 _CF_forwarding_prep_0 + 96
5 dyna 0x00000001026efefc main + 176
6 dyld 0x00000001aa75bf28 start + 2236
)
libc++abi: terminating due to uncaught exception of type NSException
Abort trap: 6
[Mac M2 Pro 12CPU, Ventura 13.6, clang 15.0.0]
教本:『詳解 Objective-C 2.0 第3版』(2011年) chap.3
Objective-Cのクラス継承について学びました。
下記のコードでは[y method2]のところで循環するのではないかと思いましたが、superを呼び出した時点でスーパークラスはclass Aに固定されるようです。
今さらですが、@interfaceはヘッダファイル、@implementationはソースファイルに分割可能でしたね。Objective-Cアプリは昨年6月に初めて書いたきりなので、すっかり忘れていました。
#import <Foundation/NSObject.h>
#import <stdio.h>
@interface A: NSObject
- (void)method1;
- (void)method2;
@end
@implementation A
- (void)method1 { printf("method1 of Class A\n"); }
- (void)method2 { printf("method2 of Class A\n"); }
@end
@interface B: A
- (void)method2;
@end
@implementation B
- (void)method2 {
printf("method2 of Class B\n");
printf("self --> ");
[self method1];
printf("super--> ");
[super method2];
}
@end
@interface C: B
- (void)method1;
@end
@implementation C
- (void)method1 { printf("method1 of Class C\n"); }
@end
int main(void)
{
id x = [[B alloc] init];
id y = [[C alloc] init];
printf("--- instance of B ---\n");
[x method1];
[x method2];
printf("--- instance of C ---\n");
[y method1];
[y method2];
return 0;
}
--- instance of B ---
method1 of Class A
method2 of Class B
self --> method1 of Class A
super--> method2 of Class A
--- instance of C ---
method1 of Class C
method2 of Class B
self --> method1 of Class C
super--> method2 of Class A // 循環にならない
[Mac M2 Pro 12CPU, Ventura 13.6, clang 15.0.0]
しばらくObjective-CやSwiftを集中的に学んでいきます。教本は『詳解 Objective-C 2.0 第3版』(2011年)です。
CHAPTER02まで読みました。サンプルコードはXcodeではなくmakefileでコンパイルします。
メソッドの第1引数に変数名がない、そもそも引数そのものを変数名にしていない、というルールにVisual Basicに対する違和感と同様のものを覚えます。単に仕様が古いということでしょうか。
まあ細かいことは気にせず淡々と読み進めます。
#import "Volume.h"
@implementation Volume
- (id)initWithMin:(int)a max:(int)b step:(int)s
{
self = [super init];
if (self != nil) {
val = min = a;
max = b;
step = s;
}
return self;
}
- (int)value
{
return val;
}
- (id)up
{
if ((val += step) > max)
val = max;
return self;
}
- (id)down
{
if ((val -= step) < min)
val = min;
return self;
}
@end
[Mac M2 Pro 12CPU, Ventura 13.6, iOS 17, Xcode 15.0]
昨年22年6月に気まぐれで購入したUIKitの本を通読していきます。『iPhoneプログラミングUIKit詳解リファレンス』(2010年)という本です。300円で買えました。
まずは出版社サイトにあるサンプルコードを以下の手順で動くようにしました。
1.プロジェクトファイル内にあるpbxprojファイルをエディタで開き、iphoneos3.1の3.1を削除するか最近のiOSバージョン番号にする。
2.Interface Builderの右にあるiOSバージョン番号設定を1と同じにする。
3.AppDelegateファイルに以下のように追記する。
#import "HelloWorldAppDelegate.h"
@implementation HelloWorldAppDelegate
@synthesize window;
- (void)applicationDidFinishLaunching:(UIApplication *)application {
// Override point for customization after application launch
window.rootViewController = [UIViewController new]; // この行を追加
[window makeKeyAndVisible];
}
- (void)dealloc {
[window release];
[super dealloc];
}
@end
[iOS 17.0.3、watchOS 10.0.1]
開発中のメモアプリについてwatchOS版を改良するため、類似アプリの機能を調べています。
watchOS版では文字列を追加すると改行が消えてしまう仕様をなんとかしたいと考えています。watchOSでは複数行を扱えるTextEditorは使えず、単行用のTextFieldしか使えないためです。
類似アプリであるDraftsやBearでの実装を確認すると追加する文字列を1行として扱い、元のメモに結合させる方法で対処していました。
検証して分かったのですがDrafts、Bear共にwatchOS版はiCloudにあるデータと直接やり取りできないようです。iOS版を介してデータを取り込んでいます。その点は自製アプリの方が優れています。
私自身はiPhone、iPad、Macでメモを作成し、Apple Watchで内容を確認するという使用スタイルですから、iPhoneで一旦起動しないと最新にならないDraftsやBearは条件を満たしていないことになります。
あと起動すると同時にメモの内容が表示されるアクセス性の良さも自製アプリのウリでしょうか。DraftsのwatchOS版は起動して2タップし内容をようやく確認できます。Inboxを呼び出すコンプリケーションでは起動プラス1タップになり少しマシになります。
今のところApp Store公開は考えていませんが、出来栄え次第では2,3ドルで販売しようかな。
『Cパズルブック』(Alan R.Feuer, 1985)
[Mac M2 Pro 12CPU, macOS Ventura 13.3.1, clang 14.0.3]
問2.3のコードと出力は以下の通りです。
ここで一旦Cパズルブックを使ったプログラミング学習を休止して、アプリ製作に戻ります。
#include "stdio.h"
#define PR(x) printf("x = %g\t", (double)x)
#define NL putchar('\n')
#define PRINT1(x1) PR(x1); NL
#define PRINT2(x1, x2) PR(x1); PRINT1(x2)
int main() {
double d = 3.2, x;
int i = 2, y;
x = (y = d/i)*2;
PRINT2(x, y);
// x = (y = (d/i))*2
// x = (y = (int)1) *2
// x = 1*2
y = (x = d/i)*2;
PRINT2(x, y);
// y = (x = (d/i))*2
// y = (x = (double)1.6 *2
// y = 3
y = d * (x = 2.5/d);
PRINT1(y);
// y = d * (x = (2.5/3.2))
// y = 3.2 * 2.5/3.2
// y = 2
x = d * (y = ((int)2.9 + 1.1)/d);
PRINT2(x, y);
// x = d * (y = ((int)2 + 1.1)/d);
// x = d * (y = 3.1/3.2);
// x = d * (y = 0);
return 0;
}
x = 2 x = 1
x = 1.6 x = 3
x = 2
x = 0 x = 0
『Cパズルブック』(Alan R.Feuer, 1985)
[Mac M2 Pro 12CPU, macOS Ventura 13.3.1, clang 14.0.3]
問2.2.4-6のコードと出力は以下の通りです。
特記すべきことはありません。
少しダレてきたので、他の教本に一旦移る予定です。Cパズルブックは第1章、第6章、第2章を順に完遂する形となり、キリとしては特に問題ないでしょう。何らかのタイミングでまた戻ってきます。
#include "stdio.h"
#define PR(x) printf("x = %.8g\t", (double)x)
#define NL putchar('\n')
#define PRINT4(x1, x2, x3, x4) PR(x1); PR(x2); PR(x3); PR(x4); NL
int main() {
double d;
float f;
long l;
int i;
// 問2.2.4
d = f = l = i = (double)100/3;
// (d = (f = (l = (i = (double)100/3))))
// (d = (f = (l = ((integer)100/3)))) and i=33
// (d = (f = (long)33)) and l=33
// (d = (float)33) and f=33
// ((double)33) and d=33
PRINT4(i, l, f, d);
// 問2.2.5
i = l = f = d = (double)(100000/3);
// (i = (l = (f = (d = (33333)))))
// (i = (l = (f = (double)33333))) and d=33333
// (i = (l = (float)33333)) and f=33333
// (i = (long)33) and l=33333
// ((integer)33) and i=33333
PRINT4(i, l, f, d);
// 問2.2.6
d = f = l = i = 100000/3;
// (d = (f = (l = (i = 100000/3))))
// (d = (f = (l = ((integer)33333)))) and i=33333
// (d = (f = (long)33)) and l=33333
// (d = (float)33) and f=33333
// ((double)33) and d=33333
PRINT4(i, l, f, d);
return 0;
}
x = 33 x = 33 x = 33 x = 33
x = 33333 x = 33333 x = 33333 x = 33333
x = 33333 x = 33333 x = 33333 x = 33333