SICPを読み始めた

SICPを何年か前に読んでいたのですが、途中まで読んでそのまま積ん読になってました。そのことをふと思い出したのでまた読み始めております。読みながらコード書く時の環境についての日記です。

SICPとは計算機プログラムの構造と解釈という本の略称。

ja.wikipedia.org

翻訳したPDFを公開してくれている方がおり、とても感謝です。

github.com

どんな感じで読もうか

REPLを使う( Guile + rlwrap )

読みながらコード写経したくなったり問題を解きたいときがあります。そういうときはGuileのREPLを使ってます。PCはmacchromebookか日によって。

www.gnu.org

guile で起動して、 ,h でヘルプ、 ,q で終了。

$ guile
scheme@(guile-user)> (+ 1 2)
$1 = 3
scheme@(guile-user)> ,h
Help Commands [abbrev]:

 ,help [all | GROUP | [-c] COMMAND]
                              [,h] - Show help.

...

scheme@(guile-user)> ,q

このままだと、 Ctrl-pCtrl-a が効かず辛いのでrlwapを使います。rlwrapは対話モードで動くようなコマンドにreadlineの機能を後付けして、コマンド履歴を Ctrl-p で遡れるようにしたりEmacsキーバインドを使えるようにしてくれます。

github.com

guile単体で起動して Ctrl-p すると以下のようになる。

$ guile
scheme@(guile-user)> (+ 1 2)
$1 = 3
scheme@(guile-user)> ; Ctrl-pを入力すると...
scheme@(guile-user)> ^[[A  ; Ctrl-p が使えない!

rlwrapといっしょに使う。rlwrap便利!

$ rlwrap guile
scheme@(guile-user)> (+ 1 2)
$1 = 3
scheme@(guile-user)> ; Ctrl-pを入力すると...
scheme@(guile-user)> (+ 1 2)  ; 履歴が使える!

ファイルに書いて読み込む

関数の行数が増えてくるとREPLで記述するのが辛いし間違えて消しちゃったら悲しいので、VS Codeで書いてファイルに保存してからREPLで読み込んで実行してます。

VS Codescheme拡張機能でコードハイライトとインデントづけを楽に。

marketplace.visualstudio.com

rlwrapの -c オプションを使うとファイル名の補完が効くので便利。

;; fib.scm

(define (fib x)
  (cond ((= x 0) 0)
        ((= x 1) 1)
        (else (+ (fib (- x 1)) (fib (- x 2)))))
)
$ rlwrap -c guile
scheme@(guile-user)> (load "fib.scm") ; tabでファイル名の補完が効く!
scheme@(guile-user)> (fib 5)
$1 = 5

,trace

,trace使うとこんな風に表示される。

scheme@(guile-user)> ,trace (fib 5)
trace: (fib 5)
trace: |  (fib 3)
trace: |  |  (fib 1)
trace: |  |  1
trace: |  |  (fib 2)
trace: |  |  |  (fib 0)
trace: |  |  |  0
trace: |  |  |  (fib 1)
trace: |  |  |  1
trace: |  |  1
trace: |  2
trace: |  (fib 4)
trace: |  |  (fib 2)
trace: |  |  |  (fib 0)
trace: |  |  |  0
trace: |  |  |  (fib 1)
trace: |  |  |  1
trace: |  |  1
trace: |  |  (fib 3)
trace: |  |  |  (fib 1)
trace: |  |  |  1
trace: |  |  |  (fib 2)
trace: |  |  |  |  (fib 0)
trace: |  |  |  |  0
trace: |  |  |  |  (fib 1)
trace: |  |  |  |  1
trace: |  |  |  1
trace: |  |  2
trace: |  3
trace: 5

末尾再帰の形に書き換えると、反復処理になってることが分かる。ちなみに fib 内部で fib-iter を記述すると ,trace の出力にでてこない、残念。出力させる方法あるのかな。

;; fib.scm

(define (fib x)
  (fib-iter x 1 0)
)

(define (fib-iter x a b)
  (cond ((= x 0) b)
        (else (fib-iter (- x 1) (+ a b) a)))
)
scheme@(guile-user)> ,trace (fib 5)
trace: |  (fib 5)
trace: |  (fib-iter 5 1 0)
trace: |  (fib-iter 4 1 1)
trace: |  (fib-iter 3 2 1)
trace: |  (fib-iter 2 3 2)
trace: |  (fib-iter 1 5 3)
trace: |  (fib-iter 0 8 5)
trace: |  5

おしまい

こんな感じで読み進めて行こう。問題解いたらとりあえずgistに追加していこっかなぁ。

_SICPの問題等を解いていく_1-3章.md · GitHub

Android Gradle Pluginを4.0.0から4.0.1へアップデート ( Android11 の Package visibility )

Android Gradle Pluginのバージョン4.0.0から4.0.1へアップデート。何が変わったのでしょうか? https://developer.android.com/studio/releases/gradle-plugin#4-0-0

This minor update supports compatibility with new default settings and features for package visibility in Android 11.

Package visibilityの管理ができるようになりました!

なんだそれは?調べてみると、他のアプリとの連携を細かく制御できるようです。 developer.android.com

アプリのtargetがAndroid 11 (API Level 30)以上で、他のアプリと連携したい場合、manifestファイルに <queries> という要素を書かないといけない場合ができたようです。


例えば、PackageManagerで情報を取得するケースを試してみます。

targetSdkVersion 30 のアプリA ( info.bati11.example.a )とアプリB ( info.bati11.example.b ) を用意する。アプリAからPackageManagerを使ってアプリBの情報を取得してみることにする。

package info.bati11.example.a

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        findViewById<Button>(R.id.button).setOnClickListener {
            val packageName = "info.bati11.example.b"
            Log.i(
                "test",
                packageManager.getPackageInfo(packageName, PackageManager.GET_ACTIVITIES).toString()
            )
        }
    }
}

API Level29のエミュレータで試すと問題なし。

2020-09-14 23:50:27.850 5962-5962/info.bati11.example.a I/test: PackageInfo{19d4329 info.bati11.example.b}

API Level30のエミュレータで試すと以下のExceptionが発生!

2020-09-14 23:59:17.151 5734-5734/info.bati11.example.a E/AndroidRuntime: FATAL EXCEPTION: main
    Process: info.bati11.example.a, PID: 5734
    java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:602)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
     Caused by: java.lang.reflect.InvocationTargetException
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947) 
     Caused by: android.content.pm.PackageManager$NameNotFoundException: info.bati11.example.b
        at android.app.ApplicationPackageManager.getPackageInfoAsUser(ApplicationPackageManager.java:206)
        at android.app.ApplicationPackageManager.getPackageInfo(ApplicationPackageManager.java:178)
        at info.bati11.example.a.MainActivity$onCreate$1.onClick(MainActivity.kt:20)
        at android.view.View.performClick(View.java:7448)
        at android.view.View.performClickInternal(View.java:7425)
        at android.view.View.access$3600(View.java:810)
        at android.view.View$PerformClick.run(View.java:28305)
        at android.os.Handler.handleCallback(Handler.java:938)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loop(Looper.java:223)
        at android.app.ActivityThread.main(ActivityThread.java:7656)
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947) 

アプリAのAndroidManifest.xmlに <queries> を記述してみる。

<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="info.bati11.example.a">
    <queries>
        <package android:name="info.bati11.example.b" />
    </queries>

    ...

これでAPI Level30のエミュレータでもOK!

2020-09-15 00:13:56.601 2388-2388/info.bati11.example.a I/test: PackageInfo{7ea0aab info.bati11.example.b}


ちなみに、アプリAの targetSdkVersion30 から 29 に変えれば例外は発生しませんでした。

また、 startActivity でアプリAからアプリBを起動する場合に <query> は必要なかったです。

公式ドキュメントにも記載されてますね。

https://developer.android.com/training/basics/intents/package-visibility#automatic

Note: If your app targets Android 10 (API level 29) or lower, all apps are visible to your app automatically.

...

In addition, you can start another app's activity using either an implicit or explicit intent, regardless of whether that other app is visible to your app.

シェルソートの動きを見てみる

Aizu Online Judge の Shell Sort の問題 をやってて、なんか分かった気がしないなぁと思ってました。

Aizu Online Judgeの一部の問題は、本だと解説されてたりします。

計算量もどうしたらいいんだろと調べたところ、単純ではないようです。

qiita.com

まずは雰囲気だけでもサクッと...と思ってJavaScriptで挿入ソートとシェルソートを並べてみました。うむ、確かにシェルソートの方が速くソートできてる。

See the Pen Insertion Sort & Shell Sort by kariyayo (@kariyayo) on CodePen.