Raspberry Pi3 Model BとRaspberryPI用3.5inchタッチ液晶(LCD)のセットアップ

Raspberry Pi3 Model Bを購入したついでにタッチ液晶(LCD)も購入したのですが、 ちょっと設定に苦戦したのブログに残しておきます。

f:id:rbtnn:20170204235008j:plain

買ったもの

やり方

1) イメージダウンロード

まず、Raspberry Piの公式ページからRaspbianのイメージをダウンロードします。 ここで気をつけたいのがNOOBSじゃなくてRaspbianのイメージを使うことです。 現時点(2017年2月4日)では、NOOBSだとタッチ液晶(LCD)のパッチの相性が悪くてうまく動かず、Raspbianだとうまく動きました。

f:id:rbtnn:20170205001031p:plain

ダウンロードしたファイル(2017-01-11-raspbian-jessie.zip)を解凍します。ダウンロードページに書いてある通り7ZipThe Unarchiverで解凍するのが楽でした。


2) SDカードに書き込み

SDカードをフォーマットし、ddコマンドでイメージを書き込みます。

f:id:rbtnn:20170205003544p:plain

※ dfコマンドでは「/dev/disk2s2」になっていますが、ddコマンドで書き込むときは「/dev/rdisk2」にします(先頭にrを付け、末尾のs2を取り除く)。


3) Raspberry Pi起動

USBキーボード、USBマウス、SDカード、有線LANケーブル、HDMIケーブルをRaspberry Piに繋ぎ、最後にMicro-USBケーブルを繋ぎます。 SDカードに正しくイメージが書き込まれていれば、Micro-USBケーブルを繋ぐだけで勝手に起動してくれます。 LANが有線であれば特に設定を行うことなく、インターネットに接続できるはずです。

f:id:rbtnn:20170205005715j:plain


4) LCDのパッチを当てる

Raspberry Pi上でTerminalを起動し、以下のコマンドを順に実行します。

$ sudo raspi-config
$ wget http://www.waveshare.com/w/upload/4/4b/LCD-show-161112.tar.gz
$ tar zxf LCD-show-161112.tar.gz
$ cd ./LCD-show
$ ./LCD35-show

sudo raspi-configの際は、

  1. 「Expand Filesystem」を選択
  2. 「Boot Option」を選択して「Desktop Autologin」を選択

を行います。

そして、Raspberry Piにタッチ液晶を接続し、再起動して完了です。

f:id:rbtnn:20170204235024j:plain

ちなみに、3.5inchのタッチ液晶に対してLCD32-showを実行してしまうと再起動後Raspberry Piの起動が失敗します。 LCD??-showのコマンドはくれぐれも間違わないように!

参考にしたもの

Vimconf 2016に参加した。

Vimconf2016に参加してきました。 本編は午後からなんだけど、一応スタッフということもあって午前中から会場入りした。

http://vimconf.vim-jp.org/2016/

http://vim-jp.org/blog/2016/10/31/announce-vimconf2016-before-event.html

発表者の各内容は公式サイトにあるので省きますが、参加して思ったことなどをいくつか。

  • vim-mode-plus for Atom editor」はt9mdさんのVimに対する理解度がやばかった。この発表、「テキストオブジェクト」や 「モーション」などのVim専門用語がバンバン使って話されていて、「これ理解して聴いている人、この中の何割くらいだ?」って思うほど濃かった。
  • koronさんは「Vimの日本語ドキュメント」について発表していたけど、backspacefm vim-sideの次のネタなんじゃ?って思っていながら聴いていたw。次のbackspacefm vim-side #3も同じネタ話すんかな?日本語入力だったけ?

http://backspace.fm/

  • Go関連の話が多かった。
  • koronさん提供のVimリストバンド。 f:id:rbtnn:20161106180641j:plain

  • 技術イベントでおなじみの寿司🍣 とピザ🍕。今回は綺麗に食べきれた。ジュースは1.5Lのが8本前後余ったけど。 f:id:rbtnn:20161106180638j:plain

  • 私作成の名札。前年度と比べるとちょっと品質向上した。 f:id:rbtnn:20161106180634j:plain

  • 発表者のスライドの文字サイズが小さい問題。Vimconf2015のujmさんのスライドの文字サイズくらいがよさそう、今後の参考にしたい。

docs.google.com

  • セキココと会場のイスレイアウトの相性の悪さ。
  • カメラ提供、vikkeさんありがとうございました。
  • b4b4r07さんのアイコンと実際の本人とのギャップ。
  • AP回復アイテム(座布団とか)が必要。

以上、お疲れ様でした。

ファイルに保存されたマクロを動的にロードする方法(Excel VBA)

以下のコードでファイルに保存されたマクロを動的にロード出来ます。 ちょっとめんどくさいのは、デフォルト設定のExcelでは「プログラミングによる Visual Basic プロジェクトへのアクセスは信頼性に欠けます」の実行時エラーになってしまい、実行できません。 なので、「マクロの設定」の「VBA プロジェクトオブジェクトモデルへのアクセスを信頼する」をチェックしないといけません。

f:id:rbtnn:20161002013544p:plain

ThisWorkbookシート

Option Explicit

' ---------------------------------------------
' ファイルに保存されたマクロを動的にロードする。
' ---------------------------------------------

Private Sub Workbook_Open()
    Call ClearModules
    Call ImportModule(ThisWorkbook.Path & "\ModMain.bas")
End Sub

Private Sub Workbook_Close()
    Call ClearModules
End Sub

Private Sub ImportModule(ByVal strFilePath As String)
    Dim objFS As Object
    Set objFS = CreateObject("Scripting.FileSystemObject")
    If objFS.FileExists(strFilePath) Then
        ThisWorkbook.VBProject.VBComponents.Import strFilePath
    Else
        Call MsgBox("モジュールが存在しません。" & vbCrLf & strFilePath)
    End If
    Set objFS = Nothing
End Sub

Private Sub ClearModules()
    Dim varComponent As Variant
    For Each varComponent In ThisWorkbook.VBProject.VBComponents
        If varComponent.Type = 1 Then
            ThisWorkbook.VBProject.VBComponents.Remove varComponent
        End If
    Next
End Sub

' 【Excel】実行エラー「プログラミングによる Visual Basic プロジェクトへのアクセスは信頼性に欠けます」の対策
' http://saku-saku-pc.com/excel/プログラミングによるアクセス/

Microsoft Visual Studio 14.0でVimをビルドする。

今までMacBook AirのTerminal上でvimをビルドするか、もしくはWindowsMinGWでビルドしていました。 今回、Microsoft Visual Studio 14.0でビルドをしてみました。 で、ビルドしてみたけど結構はまりどころがあったのでここに適当にまとめます。

まず、最終的にできた私のビルドスクリプトを載せておきます。

vimbuild.bat

@echo off

set PATH=%SystemRoot%
set PATH=%PATH%;%SystemRoot%\System32
set PATH=%PATH%;%SystemRoot%\System32\Wbem
set PATH=%PATH%;C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin

set INCLUDE=
set LIBPATH=

call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\vcvars32.bat"

set OPT1= IME=yes MBYTE=yes ICONV=yes CSCOPE=yes NETBEANS=no
set OPT2= PLATFORM=x86 SDK_INCLUDE_DIR="C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Include" XPM=no

pushd .\vim\src
nmake -f Make_mvc.mak %OPT1% %OPT2% GUI=no clean
nmake -f Make_mvc.mak %OPT1% %OPT2% GUI=yes clean
nmake -f Make_mvc.mak %OPT1% %OPT2% GUI=no
nmake -f Make_mvc.mak %OPT1% %OPT2% GUI=yes
popd

はまりどころ

Microsoft Visual Studio 14.0でVimをビルドする上で注意した方がいいオプションが以下になります。

INCLUDE, LIBPATH

これは直接的にはVimに関係ないけど、vcvars32.batINCLUDELIBPATHにどんどん値を追加していくようになっていて、 vcvars32.batを20回くらい呼ぶと限界を超え、以下のエラーになります。

入力行が長すぎます。
コマンドの構文が誤っています。

なので、vcvars32.batを呼ぶ前に初期化しておくとよいです。

set INCLUDE=
set LIBPATH=

call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\vcvars32.bat"

PLATFORM

私のビルド環境がPLATFORM=MCDだったのですが、Make_mvc.makはMCDのプラットフォームに対応してないっぽいので 以下のエラーになってしまいました。

Microsoft(R) Program Maintenance Utility Version 14.00.23918.0
Copyright (C) Microsoft Corporation.  All rights reserved.

Make_mvc.mak(228) : fatal error U1050: *** ERROR Unknown target platform "MCD". Make aborted.
Stop.

なので、私は対応しているPLATFORM=x86にして対処してみました。

PLATFORM=x86

SDK_INCLUDE_DIR

ビルドにはWin32.makを必要らしく、SDK_INCLUDE_DIRを設定していないと以下のエラーになってしまいます。

Microsoft(R) Program Maintenance Utility Version 14.00.23918.0
Copyright (C) Microsoft Corporation.  All rights reserved.

Make_mvc.mak(254) : fatal error U1052: ファイル 'Win32.mak' が見つかりません。
Stop.

なので、SDK_INCLUDE_DIRWin32.makがあるフォルダを指定します。

SDK_INCLUDE_DIR="C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Include"

XPM

Vimはpre-compiledなXPMがレポジトリに含まれていて、それがLIBC.libにリンクしているみたいです。

最近のMicrosoft Visual StudioはLIBC.libを含んでいないため、以下のようなエラーが出てしまいます。

link /RELEASE /nologo /subsystem:windows /LTCG:STATUS -out:gvim.exe .\ObjGi386\blowfish.obj  .\ObjGi386\buffer.obj
.\ObjGi386\charset.obj  .\ObjGi386\crypt.obj  .\ObjGi386\crypt_zip.obj  .\ObjGi386\diff.obj  .\ObjGi386\digraph.obj
.\ObjGi386\edit.obj  .\ObjGi386\eval.obj  .\ObjGi386\ex_cmds.obj  .\ObjGi386\ex_cmds2.obj  .\ObjGi386\ex_docmd.obj
.\ObjGi386\ex_eval.obj  .\ObjGi386\ex_getln.obj  .\ObjGi386\fileio.obj  .\ObjGi386\fold.obj  .\ObjGi386\getchar.obj
.\ObjGi386\hardcopy.obj  .\ObjGi386\hashtab.obj  .\ObjGi386\json.obj  .\ObjGi386\main.obj  .\ObjGi386\mark.obj
.\ObjGi386\mbyte.obj  .\ObjGi386\memfile.obj  .\ObjGi386\memline.obj  .\ObjGi386\menu.obj  .\ObjGi386\message.obj
.\ObjGi386\misc1.obj  .\ObjGi386\misc2.obj  .\ObjGi386\move.obj  .\ObjGi386\normal.obj  .\ObjGi386\ops.obj  .\ObjGi386\option.obj
.\ObjGi386\os_mswin.obj  .\ObjGi386\winclip.obj  .\ObjGi386\os_win32.obj  .\ObjGi386\pathdef.obj  .\ObjGi386\popupmnu.obj
.\ObjGi386\quickfix.obj  .\ObjGi386\regexp.obj  .\ObjGi386\screen.obj  .\ObjGi386\search.obj  .\ObjGi386\sha256.obj
.\ObjGi386\spell.obj  .\ObjGi386\syntax.obj  .\ObjGi386\tag.obj  .\ObjGi386\term.obj  .\ObjGi386\ui.obj  .\ObjGi386\undo.obj
.\ObjGi386\window.obj  .\ObjGi386\vim.res .\ObjGi386\gui.obj  .\ObjGi386\gui_beval.obj  .\ObjGi386\gui_w32.obj  .\ObjGi386\os_w32exe.obj
.\ObjGi386/if_cscope.obj  .\ObjGi386/channel.obj  .\ObjGi386/xpm_w32.obj .\ObjGi386\version.obj oldnames.lib kernel32.lib advapi32.lib
shell32.lib gdi32.lib  comdlg32.lib ole32.lib uuid.lib /machine:i386 gdi32.lib version.lib   winspool.lib comctl32.lib advapi32.lib
shell32.lib  /machine:i386  libcmt.lib  user32.lib          WSock32.lib xpm\x86\lib\libXpm.lib /PDB:gvim.pdb -debug
LINK : fatal error LNK1104: ファイル 'LIBC.lib' を開くことができません。
NMAKE : fatal error U1077: '"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\BIN\link.EXE"' : リターン コード '0x450'
Stop.

なので、XPM=noにして対処しました。

環境

dart:core Stringの関数について

dart:core Stringの関数について

ここに書いてある関数について適当に日本語化&例を載せてまとめます。

https://api.dartlang.org/1.13.0/dart-core/String-class.html

まとめるほどでもないけど、単純にどんな言語でも文字列操作大事だし、まぁ単純にアウトプットしたいだけです。 あと、Iterableをリストと表記したりかなり雑です。

コンストラクタ

String.fromCharCode(int charCode) factory

数値から文字列を生成する。

main(){
  var c = new String.fromCharCode(0x41);
  print(c);
  // A
}

String.fromCharCodes(Iterable<int> charCodes, [int start = 0, int end]) factory

数値のリストから文字列を生成する。このリストの始まりと終わりを指定することも可能。

main(){
  var ns = [0x41, 0x42, 0x43, 0x44];
  print(new String.fromCharCodes(ns));
  // ABCD
  print(new String.fromCharCodes(ns, 1));
  // BCD
  print(new String.fromCharCodes(ns, 1, 2));
  // B
}

String.fromEnvironment(String name, {String defaultValue}) factory

pub build時に--defineで定義した値を取得する。

main(){
  // `pub build --define releasemode=true`の場合

  print(const String.fromEnvironment('releasemode'));
  // true
}

プロパティ

codeUnits -> List<int> read-only

文字列からUTF-16のコードの変更不可なリストを返す。

main(){
  var xs = 'hi'.codeUnits;
  print(xs);
  // [104, 105]

  // エラー!
  // xs[0] = 0x41;
}

hashCode -> int read-only

文字列のハッシュ値を返す。

main(){
  var hash = 'hi'.hashCode;
  print(hash);
  // 793667904
}

isEmpty -> bool read-only

空文字列ならtrueを返す。

main(){
  print('hoge'.isEmpty);
  // false
  print(''.isEmpty);
  // true
  print('   '.isEmpty);
  // false
}

isNotEmpty -> bool read-only

空文字列でないならtrueを返す。

main(){
  print('hoge'.isNotEmpty);
  // true
  print(''.isNotEmpty);
  // false
  print('   '.isNotEmpty);
  // true
}

length -> int read-only

文字列の長さを返す。

main(){
  print('hoge'.length);
  // 4
  print(''.length);
  // 0
}

runes -> Runes read-only

文字列のUnicodeコードのイテレータを返す。

main(){
  print('hoge'.runes);
  // (104, 111, 103, 101)

  for(var c in 'hoge'.runes){
    print(c);
  }
  // 104
  // 111
  // 103
  // 101
}

runtimeType -> Type read-only, inherited

型名を返す。

class A{}
main(){
  var a = new A();
  print(a.runtimeType);
  // A
  print('hoge'.runtimeType);
  // String
}

オペレータ

operator *(int times) -> String

文字列をtimes回繰り返した文字列を返す。

main(){
  print('abc' * 2);
  // abcabc
}

operator +(String other) -> String

文字列同士を結合する。

main(){
  print('abc' + 'def');
  // abcdef
}

operator ==(Object other) -> bool

文字列が同じであるか判定する。

main(){
  var a = 'abc';
  var b = 'abc';
  var c = 'def';
  print(a == b);
  // true
  print(a == c);
  // false
  print(b == c);
  // false
}

operator [](int index) -> String

文字列のindex番目の文字を取得する。(0オリジン)

main(){
  var str = 'abc';
  print(str[0]);
  // a
  print(str[2]);
  // c
}

メソッド

あとで書く。

Surface Pro 4とArc Touch Mouse for Surface

Surface Pro 4とArc Touch Mouse for Surfaceを買ったのだけど、Arc Touch Mouseのマウスホイールの振動音がうるさい。 なので、その対処方法を書いておく。

Wedge Touch Mouse and Arc Touch Mouse for Surface

手順

  1. スタートメニューからストアを開く。
  2. 検索のテキストボックスに「Arc Touch Bluetooth Mouse」を入力して検索する。
  3. アプリをインストールする。Microsoftアカウントが必要。
  4. スタートメニューから「Arc Touch Bluetooth Mouse」を選択して起動する。
  5. 振動/サウンドをOFFにする。

f:id:rbtnn:20151115150253p:plain

f:id:rbtnn:20151115150538p:plain

www.microsoftstore.com

www.microsoftstore.com

「Land of lisp」を読んだのでCommon Lispのまとめ

Land of lispを読んだので、Common Lispについてまとめてみます(以下Common LispのことをLispと表記しています)。 一応私はLisp初心者なので間違いがあるかもしれません。

www.amazon.co.jp

clisp

まず、Lispの処理系ですが以下のコマンドで一発でインストールできます (clispがメジャーらしい)。

Mac

>brew install clisp

Windows

>choco install clisp

clispはREPLもできますが、以下のようにソースファイルをインタプリタすることができます。

>clisp a.lisp

clispはソースファイルをコンパイルできますが、出力されるのはネイティブコードではなくバイトコードになります。

>clisp -c a.lisp -o a.fas
>clisp a.fas

Lispについて

Lispのコードは簡単にいうとポーランド記法の式を括弧で括って表現されたものです。括弧で括られたものをリストと呼びます。

(+ 1 2)
; 3

コメントアウト

Lisp;コメントアウトすることができます。

; これはコメントです。

コマンドモードとデータモード

Lispにはコマンドモードデータモードというものがあって、以下のようなリストコマンドモードという状態下にあります。 コマンドモードでのリストフォームとも呼び、フォームの1つ目の要素(ここでは+に当たる)をコマンドと呼びます。 このコマンドは関数やマクロなど特別なものでないといけません。

(+ 1 2)
; 3

データモードリスト'を付けた形となります。コマンドモードと異なり、1つ目の要素がコマンドである必要はありません。 なのでデータモードでは、リストとしてそのまま評価されます。

'(+ 1 2)
; (+ 1 2)
'(1 2 3)
; (1 2 3)
'(1 2 3 (4 5))
; (1 2 3 (4 5))
; '(...)内のものはすべてデータモードで評価される。

シンボル

Lispにはシンボルという独立した値を表すトークンが存在します。 シンボルに使える文字はアルファベット、数字、そして+ - / * = < > ? ! _になります。 また、シンボルは基本的にアルファベットの大文字小文字を区別しません。 ただし、|を使うことで大文字小文字を区別したシンボルを作成することも出来ます。

(princ 'hoge)
; HOGE
(princ '|Hello World|)
; Hello World

数値

Lisp数値には整数と浮動小数点数の2種類が存在します。 小数点が含まれていれば浮動小数点数、小数点が含まれていなければ整数として扱われます。 また、Lispでは11.0が区別されます。

(princ 12)
; 12
(princ 1.23)
; 1.23

文字列

Lisp文字列は文字の集まりを"で括って表します。 \"を含めたい場合には\でエスケープします。

(princ "hello")
; hello
(print "hello")
; "hello"

文字

Lispの文字リテラル#\を付けることで表すことが出来ます。 特殊な文字(改行、タブ、スペース、など)はそれぞれ#\newline#\tab#\spaceで表すことが出来ます。

(princ #\c)
; c
(princ #\newline)
(princ #\tab)
(princ #\space)

グローバル変数

グローバル変数を定義するにはdefparameterもしくはdefvarを使います。

(defparameter *hoge* 12)
(defvar *hoge* (+ 3 4))

defparameterdefvarの違いは複数回呼ばれた時に上書きされるかどうかです。

defparameterはどんどん上書きしていくのに対して、

(defparameter *hoge* 1)
(print *hoge*)
; 1
(defparameter *hoge* 2)
(print *hoge*)
; 2

defvarは初回の値を保ち続けます。

(defvar *hoge* 1)
(print *hoge*)
; 1
(defvar *hoge* 2)
(print *hoge*)
; 1

Lispではグローバル変数*で括る文化があるらしい。
また、Lispではグローバル変数のことをダイナミック変数スペシャル変数とも呼びます。

関数の定義

関数を定義するには(defun 関数名(引数) 本体)の形式で定義することができます。

(defun add(a b) (+ a b))
(add 1 2)
; 3

ローカル変数とローカル関数

ローカル変数を定義するには(let ((変数名 値) ...) 本体)の形式で定義することができます。

(let ((x 3) (y 4)) (* x y))
; 12

ローカル関数を定義するには(flet ((関数名(引数) 本体) ...) 本体)の形式で定義することができます。

(flet (
    (f(x) (1+ x))
  )
  (f 3))
; 4

fletは同レベルで定義されたローカル関数を参照することができません(再帰すらできません)。 なので、同レベルで定義されたローカル関数を参照するローカル関数を定義するにはlabelsを使います。

別のローカル関数を呼ぶ

(labels (
    (f(x) (1- x))
    (g(x) (* (f x) 2))
  )
  (g 3))
; 4

自身のローカル関数を呼ぶ

(labels (
       (f(xs) 
           (if xs
             (+ (car xs) (f (cdr xs)))
             0)))
  (f '(1 2 3)))
; 6

コンスセル

Lispにはコンスセルという単位があります。 コンスセルは値を格納できる箱(スロットと呼ばれる)が2つあるようなものです。

コンスセルのイメージ

 [|]
 / \
1   2

ちなみにリストコンスセルの集まりです。 リストコンスセルが連続して最後の要素がnilという特別な値で終わるもののことを指します。

リストのイメージ

 [|]
 / \
1  [|]
   / \
  2  [|]
     / \
    3  nil

コンスセルを作成するにはcons.で作成することが出来ます。

(cons 1 2)
; (1 . 2)
'(1 . 2)
; (1 . 2)

リストコンスセルの集まりなので、以下のようにリスト作ることも出来ます。

'(1 . (2 . nil))
; (1 2)
'(1 2)
; (1 2)

真理値

Lispには特別なシンボルTnilがあります。Tが真値、nilが偽値と評価されます。

(if T 1 2)
; 1
(if nil 1 2)
; 2

実はLispではnil'nil()'()が同じ意味を持ち偽値と評価され、これ以外のすべて値が真値と評価されます。

(if '(1 2) 1 2)
; 1
(if () 1 2)
; 2

carとcdr

Lispにはリストから値を取り出す関数carcdrがあります。 carは先頭の要素を返し、cdrは残りの要素をリストで返します。

(car '(1 2 3 4 5))
; 1
(cdr '(1 2 3 4 5))
; (2 3 4 5) 

carcdrも、正確にはコンスセルを扱う関数なので以下のようにコンスセルから値を取り出すことも出来ます。

(car '(1 . 2))
; 1
(cdr '(1 . 2))
; 2

リストcdrを適用すると末尾にnilが付属するため結果がリストになりますが、 コンスセルcdrを適用しても末尾にnilが付属しないため結果がリストになるとは限りません。

(cdr '(1 2))
; (2)
(cdr '(1 . 2))
; 2
(cdr '(1 . (2 . nil)))
; (2)

Lispにはcarcdrのほかにcdarのような/c[ad]+r/の関数が用意されています(どのくらいの長さまで用意されているかは処理系による)。

(cadr '(1 2 3 4))
; 2
(cadddr '(1 2 3 4))
; 4
(cddddr   '(1 2 3 4))
; nil
(caar   '((1 2) 3 4))
; 1

eqとequal

Lispには同じかどうか比較する関数がいくつか存在します。 とりあえず、以下のルールにしたがって使えばよいらしい。

  1. シンボル同士は常にeqで比較すべし。
  2. シンボル同士でなければequalを使え。

ちなみにequalは内部で型分岐をしていて、数値なら=、文字列ならstring-equal、文字ならchar-equalを使っています。 なので、処理速度を求めるなら、これらを直接呼んだ方が良いです。

準クオート

Lisp'によってコマンドモードからデータモードに移行することはできますが、 データモードからコマンドモードに移行することはできません。 そのため、Lispには`,を使ってデータモードからコマンドモードへと移行出来る仕組みが存在します。

`(1 2 ,(car '(3 4)) 5)
; (1 2 3 5)

このように、`データモードが開始され、,で一時的にコマンドモードへと戻ることが出来ます。

関数オペレータ

Lispは関数を値として扱うことが出来ます。例えば、mapcar関数は関数とリストを1つずつ受け取り、 そのリストの各要素に関数を適用した新たなリストを返す関数です。 その際、関数を値として認識させるために関数オペレータ#'もしくはfunction関数を使用します。 mapcar関数のような関数を値として扱うような関数を高階関数と呼びます。

(mapcar #'sqrt '(1 2 3 4))
; (1 1.4142135 1.7320508 2)
(mapcar (function car) '((a b) (c d) (e f) (g h)))
; (A C E G)

ラムダ式

Lispで関数を定義するにはdefun関数のような関数を使います。しかし、これらの関数は関数名を指定しなくてはなりません。 高階関数を使う際にわざわざ関数名を指定するのは億劫なので、Lispにはラムダ式というlambdaを使って即座に名前なしの関数を作成することが出来ます。

(mapcar (lambda (x) (* x 2)) '(1 2 3 4))
; (2 4 6 8)

循環リスト

リストの末尾のnilリストの先頭にしてやることで循環リストという循環したコンスセルを作成することが出来ます。

循環リストのイメージ

  +--------+
  v        |
 [|]       |
 / \       |
1  [|]     |
   / \     |
  2  [|]   |
     / \   |
    3   +--+
; clispの処理系に「これから循環リストを表示するよ」と伝える必要がある。
(setf *print-circle* t)

(defparameter *foo* '(1 2 3 4))
(setf (cddddr *foo*) *foo*)
(print *foo*)
; #1=(1 2 3 4 . #1#) 

配列

Lispにはリストの他に配列も用意されています。配列リストより要素に速くアクセスすることが出来ます。 配列を作成するにはmake-arrayで作成することが出来ます。引数にはその配列のサイズを指定します。 配列の要素を参照する方法はarefを使い、値を設定するにはarefsetfを使います。

(defparameter *ary* (make-array 3))
(print *ary*)
; #(nil nil nil)
(setf (aref *ary* 1) 3)
(print *ary*)
; #(nil 3 nil)
(print (aref *ary* 1))
; 3

ハッシュテーブル

Lispにはハッシュテーブルも用意されています(連想リストとは別物です)。 ハッシュテーブルを作成するにはmake-hash-tableを使います。 ハッシュテーブルのキーを参照する方法はgethashを使い、キーの値を設定するにはgethashsetfを使います。

(defparameter *hash* (make-hash-table))
(print *hash*)
; #S(HASH-TABLE :TEST FASTHASH-EQL) 
(setf (gethash 'foo *hash*) 3)
(print (gethash 'foo *hash*))
; 3

構造体

Lispには構造体も用意されています。 構造体を定義するには(defstruct 構造体名 メンバ...)の形式で定義することができます。 構造体が定義されると自動的に構造体を作成する関数と各メンバを参照する関数が定義されます。

(defstruct person name age)
(defparameter *p* (make-person :name "bob" :age 32))
(print *p*)
; #S(PERSON :NAME "bob" :AGE 32) 
(print (person-age *p*))
; 32

マクロ

LispにはC言語#defineのような強力なマクロが存在します。 マクロを定義するには(defmacro マクロ名 (引数) 本体)defunと似た感じで定義出来ます。

マクロ例1

(defmacro m1 (f n1 &rest rest)
  `(+ ,@rest)
  )
(print (m1 cdr 1 2 3))
; 5

&restrestに残りの引数を1つのリストにしますよと伝えるためのものです。 なのでここでは、fcdrn11rest(2 3)が入ります。 ここでrest@を付け加えることで2 3と括弧を取り除いた状態で展開することが出来ます。 よって、このマクロを展開すると(+ 2 3)という形になり、5が出力されます。

マクロ例2

(defmacro m2 (&rest rest)
  `(list ,@rest)
  )
(print (macroexpand (m2 1 2 3)))
; (1 2 3) 

macroexpandはマクロ展開後の状態を文字列として返す便利なマクロです。

マクロ例3

(defmacro m3 (f n1 &rest rest)
  `(,f ',rest)
  )
(print (m3 cdr 1 2 3 4))
; (3 4)

主な関数やマクロ

time

引数に指定された要素の評価した処理時間を求める。

(time (+ 1 2))
; Real time: 2.1E-5 sec.
; Run time: 1.7E-5 sec.
; Space: 0 Bytes

substitute-if

引数に指定された述語にマッチした要素を置換して返す。

(substitute-if #\- (lambda (x) (equal x #\3)) "1234")
; "12-4"
(substitute-if 'x (lambda (x) (eq x 'c)) '(a b c))
; (A B X) 

assoc

連想リスト(alistと呼ばれる)からキーにマッチする要素を返す。

(assoc 'hoge '((foo (1 2)) (hoge (3 4))))
; (HOGE (3 4))

push

リストを保持している変数の先頭に要素を追加する。

(defparameter *foo* '((a 1) (b 2)))
(princ *foo*)
; ((a 1) (b 2))
(push '(c 3) *foo*)
(princ *foo*)
; ((c 3) (a 1) (b 2))

setf

変数に値を代入する。

(defparameter *foo* '(1 4))
(setf *foo* (cons 7 *foo*))
(princ *foo*)
; (7 1 4)

find

リストの各要素に:keyで指定された関数を適用した結果に対して第1引数の要素を探す。

(find 'b '((a 1) (b 2) (c 3) (d 4)) :key #'car)
; (B 2)
(find 'e '((a 1) (b 2) (c 3) (d 4)) :key #'car)
; nil

remove-if-not

リストの各要素に関数を適用した結果が偽の要素を取り除いた新たなリストを返す。

; 奇数でない要素を取り除く。
(remove-if-not #'oddp '(1 2 3 4))
; (1 3)

mapcar

リストの各要素に関数を適用した新たなリストを返す。

(mapcar #'sqrt '(1 2 3 4))
; (1 1.4142135 1.7320508 2)

sqrt

平方根を計算する。

(sqrt 2)
; 1.4142135

expt

べき乗を計算する。

(expt 2 3)
; 8

if

条件分岐を行う。

(if T 1 2)
; 1
(if nil 1 2)
; 2
(if () (princ "good") (princ "bad"))
; bad

when

真の場合のみ処理を実施する。

(when T   (princ "bad"))
; bad
(when nil (princ "good"))

unless

偽の場合のみ処理を実施する。

(unless T   (princ "bad"))
(unless nil (princ "good"))
; good

cond

条件分岐を行う。

(let ((x 2))
  (cond
    ((eq x 1) "A")
    ((eq x 2) "B")
    ((eq x 3) "C")
    (t        "-")))
; "B"

case

条件分岐を行う。

(let ((x 2))
  (case x
    (1         "A")
    (2         "B")
    (3         "C")
    (otherwise "-")))
; "B"

and

AND演算を行う。

(and)
; T
(and T nil)
; nil
(and T T T T)
; T
(and nil nil nil)
; nil

or

OR演算を行う。

(or)
; nil
(or T nil)
; T
(or T T T T)
; T
(or nil nil nil)
; nil

append

リスト同士を結合する。

(append '(1 2) '(3 4))
; (1 2 3 4)
(append '(1 2) 3)
; (1 2 . 3)

member

リストから要素を探す。 もし見つかったなら、その位置から末尾までのリストを返す。

(member 1 '(1 2 3 4))
; (1 2 3 4)
(member 3 '(1 2 3 4))
; (3 4)
(member 0 '(1 2 3 4))
; nil
(member 5 '(1 2 3 4))
; nil

find-if

リストから述語にマッチする最初に見つかった要素を返す。

(find-if 'oddp '(2 3 2 2))
; 3

progn

複数の処理を1つにまとめる。

(if ()
  (princ "good")
  (progn
    (princ "too")
    (princ " ")
    (princ "bad"))
  )
; too bad

oddp

奇数ならTを返し、偶数ならnilを返す。

(oddp 1)
; T
(oddp 2)
; nil

evenp

偶数ならTを返し、奇数ならnilを返す。

(evenp 2)
; T
(evenp 1)
; nil

random

0からN-1の間の値をランダムに返す。

(random 4)
; 3
(random 4)
; 0
(random 4)
; 2
(random 4)
; 1

length

リストの長さを返す。

(length '(1 2 3 4))
; 4

nth

N番目の要素を返す(0オリジン)。

(nth 3 '(1 2 3 4))
; 4
(nth 0 '(1 2 3 4))
; 1
(nth 2 '(1 2 3 4))
; 3
(nth 6 '(1 2 3 4))
; nil

list

引数に与えられたものからリストを作ります。

(list 1 2 3 4)
; (1 2 3 4)

load

別のソースファイルを読み込みます。

(load "b.lisp")

read

ユーザーからEnterキーが押されるまで入力を受けつける。 入力された文字列に応じて、シンボル、数値、文字列などに変換して返す。

(read)
hoge
; HOGE
(read)
123
; 123
(read)
"key"
; "key"

read-from-string

read関数の文字列版。 文字列に応じて、シンボル、数値、文字列などに変換して返す。

(read-from-string "hoge")
; HOGE
(read-from-string "123")
; 123
(read-from-string "\"key\"")
; "key"

concatenate

引数に応じてシーケンス同士の連結を行う。

(concatenate 'string "hoge" "foo")
; "hogefoo"
(concatenate 'list '(2) '(1))
; (2 1)

char-downcase

文字を小文字に変換する。

(char-downcase #\C)
; #\c

char-downcase

文字を大文字に変換する。

(char-upcase #\c)
; #\C

eval

引数に指定されたデータを評価し、その値を返す。

(eval '(+ 1 2))
; 3

read-line

ユーザーからEnterキーが押されるまで入力を受ける。 入力された文字列を1つの文字列として解釈する。

(read-line)
hoge
; "hoge"
(read-line)
123
; "123"
(read-line)
"key"
; "\"key\""

print

半角スペースと改行付きで値をそのまま標準出力する。

(print "hey")
; "hey" 

prin1

print関数の半角スペースと改行が付かない版。

(prin1 "hey")
; "hey"

princ

値を人間に分かりやすい形で標準出力する。

(princ "hey")
; hey

fresh-line

直前の文字が改行出ない場合のみ、改行を出力する。

(fresh-line)
;
(fresh-line)
(fresh-line)
(fresh-line)
(fresh-line)

terpri

改行を出力する。

(terpri)
;
(terpri)
;
(terpri)
;
(terpri)
;
(terpri)
;

incf 1+

インクリメント。

(1+ 4)
; 5

decf 1-

デクリメント。

(1- 4)
; 3

ash

Bitシフト。

; 左に1bitシフトする。
(ash 11 1)
; 22
; 右に1bitシフトする。
(ash 11 -1)
; 5

終わり

まだ、loopマクロformat関数ストリーム、など説明してない項目がありますが、ある程度はまとめられたと思うので終わり。