Skip to content

Instantly share code, notes, and snippets.

@genkuroki
Last active September 22, 2020 04:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save genkuroki/1702e7be9bc41321f0f2f2198b6ed7a3 to your computer and use it in GitHub Desktop.
Save genkuroki/1702e7be9bc41321f0f2f2198b6ed7a3 to your computer and use it in GitHub Desktop.
Minimal Lisp in Julia - Part 1
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"metadata": {},
"cell_type": "markdown",
"source": "# Minimal Lisp in Julia - Part 1\n\nGen Kuroki\n\n2020-09-21\n\nReferences:\n\n* [簡易LISP処理系の実装例【各言語版まとめ】](https://qiita.com/ytaki0801/items/dcb0fe48cbeab30ac40d)\n * [簡易LISP処理系の実装例(Julia版)](https://qiita.com/ytaki0801/items/f902bd81cc8828774911)"
},
{
"metadata": {
"toc": true
},
"cell_type": "markdown",
"source": "<h1>Table of Contents<span class=\"tocSkip\"></span></h1>\n<div class=\"toc\"><ul class=\"toc-item\"><li><span><a href=\"#Implementation-example-in-Julia-of-minimal-Lisp\" data-toc-modified-id=\"Implementation-example-in-Julia-of-minimal-Lisp-1\"><span class=\"toc-item-num\">1&nbsp;&nbsp;</span>Implementation example in Julia of minimal Lisp</a></span></li><li><span><a href=\"#Execution-examples\" data-toc-modified-id=\"Execution-examples-2\"><span class=\"toc-item-num\">2&nbsp;&nbsp;</span>Execution examples</a></span><ul class=\"toc-item\"><li><span><a href=\"#雑多な例\" data-toc-modified-id=\"雑多な例-2.1\"><span class=\"toc-item-num\">2.1&nbsp;&nbsp;</span>雑多な例</a></span></li><li><span><a href=\"#4つの例\" data-toc-modified-id=\"4つの例-2.2\"><span class=\"toc-item-num\">2.2&nbsp;&nbsp;</span>4つの例</a></span></li></ul></li><li><span><a href=\"#Juliaにおける類似のコード\" data-toc-modified-id=\"Juliaにおける類似のコード-3\"><span class=\"toc-item-num\">3&nbsp;&nbsp;</span>Juliaにおける類似のコード</a></span><ul class=\"toc-item\"><li><span><a href=\"#example-1\" data-toc-modified-id=\"example-1-3.1\"><span class=\"toc-item-num\">3.1&nbsp;&nbsp;</span>example 1</a></span></li><li><span><a href=\"#example2\" data-toc-modified-id=\"example2-3.2\"><span class=\"toc-item-num\">3.2&nbsp;&nbsp;</span>example2</a></span></li><li><span><a href=\"#example3\" data-toc-modified-id=\"example3-3.3\"><span class=\"toc-item-num\">3.3&nbsp;&nbsp;</span>example3</a></span></li><li><span><a href=\"#example-4\" data-toc-modified-id=\"example-4-3.4\"><span class=\"toc-item-num\">3.4&nbsp;&nbsp;</span>example 4</a></span></li></ul></li></ul></div>"
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "VERSION",
"execution_count": 1,
"outputs": [
{
"output_type": "execute_result",
"execution_count": 1,
"data": {
"text/plain": "v\"1.6.0-DEV.928\""
},
"metadata": {}
}
]
},
{
"metadata": {},
"cell_type": "markdown",
"source": "## Implementation example in Julia of minimal Lisp"
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "struct Nil end\nconst nil = Nil()\nBase.show(io::IO, ::Nil) = print(io, \"nil\")\n\nstruct T end\nconst t = T()\nBase.show(io::IO, ::T) = print(io, \"t\")\n\n_tnil(x::Bool) = ifelse(x, t, nil) # true -> t, false -> nil\n\n# Lisp functions in Julia\n\n_eq(x, y) = x == y # eq for Julia\neq(x, y) = _tnil(_eq(x, y)) # eq for Lisp\n\n_null(x) = _eq(x, nil) # null for Julia\nnull(x) = _tnil(_null(x)) # null for Lisp\n\n_atom(x) = true\n_atom(x::Tuple) = false # atom for Julia\natom(x) = _tnil(_atom(x)) # atom for Lisp\n\ncons(x, y) = (x, y)\ncons(x, y::Tuple) = (x, y...)\n\ncar(x) = nil\ncar(x::Tuple) = x[1]\ncdr(x) = nil\nfunction cdr(x::Tuple)\n @assert length(x) != 1 \"The argument of cdr is not an S-expression.\"\n length(x) == 2 ? x[2] : x[2:end]\nend\n\ncaar(x) = car(car(x))\ncadr(x) = car(cdr(x))\ncdar(x) = cdr(car(x))\ncddr(x) = cdr(cdr(x))\n\nfor (x, y, z) in Iterators.product(fill([:a, :d], 3)...)\n cxyzr = Symbol(:c, x, y, z, :r)\n cxr, cyr, czr = Symbol.(:c, (x, y, z), :r)\n @eval $cxyzr(x) = $cxr($cyr($czr(x)))\nend\n\nquote_(x) = x # quote for Lisp\n\nappend(x, y) = _null(x) ? y : cons(car(x), append(cdr(x), y))\n\nlist(x) = cons(x, nil)\nlist(x, y...) = cons(x, list(y...))\n\npair(x, y) = nil\npair(x::Tuple, y::Tuple) = !_null(x) || !_null(y) ?\n cons(list(car(x), car(y)), pair(cdr(x), cdr(y))) : nil\n\nassoc(x, a) = x # a stands for association list\nassoc(x, a::Tuple) = _eq(caar(a), x) ? cadar(a) : assoc(x, cdr(a))\n\n# eval for Lisp\neval_(x, a=nil) = assoc(x, a) # a stands for symbol-to-value association list\nfunction eval_(sexpr::Tuple, a=nil)\n func, args = car(sexpr), cdr(sexpr)\n if _atom(func)\n _eq(func, :quote) && return quote_(car(args))\n _eq(func, :atom) && return atom(eval_(car(args), a))\n _eq(func, :eq) && return eq(eval_(car(args), a), eval_(cadr(args), a))\n _eq(func, :car) && return car(eval_(car(args), a))\n _eq(func, :cdr) && return cdr(eval_(car(args), a))\n _eq(func, :cons) && return cons(eval_(car(args), a), eval_(cadr(args), a))\n _eq(func, :cond) && return cond(args, a)\n if isa(func, Symbol)\n assocfunc = assoc(func, a)\n assocfunc != func && return eval_(cons(assocfunc, args), a)\n end\n elseif _eq(car(func), :lambda)\n p = pair(cadr(func), eval_list(args, a))\n return eval_(caddr(func), append(p, a))\n end\n error(\"Error: The car \", sprint_sexpr(func), \" of \", sprint_sexpr(sexpr), \" is not callable.\")\nend\n\ncond(args, a=nil) = nil\nfunction cond(args::Tuple, a=nil)\n arg = car(args)\n condition, action = car(arg), cadr(arg)\n _eq(eval_(condition, a), t) && return eval_(action, a)\n cond(cdr(args), a)\nend\n\neval_list(x, a=nil) = x\neval_list(x::Tuple, a=nil) = cons(eval_(car(x), a), eval_list(cdr(x), a))\n\n# Convert String code to S-expression\n\n_tok(code) = split(replace(chomp(code), r\"([()'])\"=>s\" \\1 \"))\n\nfunction _literal(x)\n x == \"nil\" && return nil\n x == \"t\" && return t\n occursin(r\"^[+-]?\\d+$\", x) && return parse(Int, x)\n Symbol(x)\nend\n\nfunction _sexpr!(tok)\n tailtok = pop!(tok)\n if tailtok == Symbol(\")\")\n tailexpr = nil\n while tok[end] != Symbol(\"(\")\n if tok[end] == Symbol(\".\")\n pop!(tok)\n tailexpr = cons(_sexpr!(tok), car(tailexpr))\n else\n tailexpr = cons(_sexpr!(tok), tailexpr)\n end\n end\n pop!(tok)\n else\n tailexpr = tailtok\n end\n if !isempty(tok) && tok[end] == Symbol(\"'\")\n pop!(tok)\n return cons(:quote, cons(tailexpr, nil))\n end\n tailexpr\nend\n_sexpr(code) = _sexpr!(_literal.(_tok(code)))\n\n# Print and execute a code and output the result \n\nsprint_sexpr(x) = string(x)\nsprint_sexpr(x::AbstractString) = \"\\\"\" * string(x) * \"\\\"\"\nsprint_sexpr(x::Symbol) = \":\" * string(x)\nfunction sprint_sexpr(x::Tuple)\n s = \"(\"\n n = length(x)\n for i in 1:n-1\n s *= sprint_sexpr(x[i]) * \" \"\n end\n if !_null(x[n])\n s *= \". \" * sprint_sexpr(x[n]) * \")\"\n else\n s = s[1:end-1] * \")\"\n end\n s\nend\nprint_sexpr(x) = print(sprint_sexpr(x))\n\nfunction lisp(code)\n s = _sexpr(code)\n y = eval_(s)\n # md = \"\"\"```commonlisp\n md = \"\"\"```lisp\n \"\"\" * chomp(code) * \"\"\"\n \\n=> \"\"\" * sprint_sexpr(y) * \"\"\"```\\n\\n\"\"\"\n sleep(0.1)\n display(\"text/markdown\", md)\nend\n\nmacro lisp_str(code) :(lisp($(esc(code)))) end",
"execution_count": 2,
"outputs": [
{
"output_type": "execute_result",
"execution_count": 2,
"data": {
"text/plain": "@lisp_str (macro with 1 method)"
},
"metadata": {}
}
]
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "code = \"(cdr (car '((1 (2 3)) 4)))\"",
"execution_count": 3,
"outputs": [
{
"output_type": "execute_result",
"execution_count": 3,
"data": {
"text/plain": "\"(cdr (car '((1 (2 3)) 4)))\""
},
"metadata": {}
}
]
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "tok = _tok(code)",
"execution_count": 4,
"outputs": [
{
"output_type": "execute_result",
"execution_count": 4,
"data": {
"text/plain": "17-element Vector{SubString{String}}:\n \"(\"\n \"cdr\"\n \"(\"\n \"car\"\n \"'\"\n \"(\"\n \"(\"\n \"1\"\n \"(\"\n \"2\"\n \"3\"\n \")\"\n \")\"\n \"4\"\n \")\"\n \")\"\n \")\""
},
"metadata": {}
}
]
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "tok = _literal.(tok)",
"execution_count": 5,
"outputs": [
{
"output_type": "execute_result",
"execution_count": 5,
"data": {
"text/plain": "17-element Vector{Any}:\n Symbol(\"(\")\n :cdr\n Symbol(\"(\")\n :car\n Symbol(\"'\")\n Symbol(\"(\")\n Symbol(\"(\")\n 1\n Symbol(\"(\")\n 2\n 3\n Symbol(\")\")\n Symbol(\")\")\n 4\n Symbol(\")\")\n Symbol(\")\")\n Symbol(\")\")"
},
"metadata": {}
}
]
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "s = _sexpr!(tok)",
"execution_count": 6,
"outputs": [
{
"output_type": "execute_result",
"execution_count": 6,
"data": {
"text/plain": "(:cdr, (:car, (:quote, ((1, (2, 3, nil), nil), 4, nil), nil), nil), nil)"
},
"metadata": {}
}
]
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "tok",
"execution_count": 7,
"outputs": [
{
"output_type": "execute_result",
"execution_count": 7,
"data": {
"text/plain": "Any[]"
},
"metadata": {}
}
]
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "s = _sexpr(code)",
"execution_count": 8,
"outputs": [
{
"output_type": "execute_result",
"execution_count": 8,
"data": {
"text/plain": "(:cdr, (:car, (:quote, ((1, (2, 3, nil), nil), 4, nil), nil), nil), nil)"
},
"metadata": {}
}
]
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "_sexpr(code) |> print_sexpr",
"execution_count": 9,
"outputs": [
{
"output_type": "stream",
"text": "(:cdr (:car (:quote ((1 (2 3)) 4))))",
"name": "stdout"
}
]
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "eval_(s)",
"execution_count": 10,
"outputs": [
{
"output_type": "execute_result",
"execution_count": 10,
"data": {
"text/plain": "((2, 3, nil), nil)"
},
"metadata": {}
}
]
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "eval_(s) |> print_sexpr",
"execution_count": 11,
"outputs": [
{
"output_type": "stream",
"text": "((2 3))",
"name": "stdout"
}
]
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "eval_(_sexpr(\"'((1 (2 3)) 4)\")) |> print_sexpr",
"execution_count": 12,
"outputs": [
{
"output_type": "stream",
"text": "((1 (2 3)) 4)",
"name": "stdout"
}
]
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "eval_(_sexpr(\"(car '((1 (2 3)) 4))\")) |> print_sexpr",
"execution_count": 13,
"outputs": [
{
"output_type": "stream",
"text": "(1 (2 3))",
"name": "stdout"
}
]
},
{
"metadata": {
"trusted": true,
"scrolled": true
},
"cell_type": "code",
"source": "eval_(_sexpr(\"(car (car '((1 (2 3)) 4)))\")) |> print_sexpr",
"execution_count": 14,
"outputs": [
{
"output_type": "stream",
"text": "1",
"name": "stdout"
}
]
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "eval_(_sexpr(\"(cdr (car '((1 (2 3)) 4)))\")) |> print_sexpr",
"execution_count": 15,
"outputs": [
{
"output_type": "stream",
"text": "((2 3))",
"name": "stdout"
}
]
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "eval_(_sexpr(\"(car (cdr (car '((1 (2 3)) 4))))\")) |> print_sexpr",
"execution_count": 16,
"outputs": [
{
"output_type": "stream",
"text": "(2 3)",
"name": "stdout"
}
]
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "eval_(_sexpr(\"(cdr '((1 (2 3)) 4))\")) |> print_sexpr",
"execution_count": 17,
"outputs": [
{
"output_type": "stream",
"text": "(4)",
"name": "stdout"
}
]
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "eval_(_sexpr(\"(car (cdr '((1 (2 3)) 4)))\")) |> print_sexpr",
"execution_count": 18,
"outputs": [
{
"output_type": "stream",
"text": "4",
"name": "stdout"
}
]
},
{
"metadata": {},
"cell_type": "markdown",
"source": "## Execution examples"
},
{
"metadata": {},
"cell_type": "markdown",
"source": "### 雑多な例"
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "lisp\"nil\"",
"execution_count": 19,
"outputs": [
{
"output_type": "display_data",
"data": {
"text/markdown": "```lisp\nnil\n=> nil```\n\n"
},
"metadata": {}
}
]
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "lisp\"t\"",
"execution_count": 20,
"outputs": [
{
"output_type": "display_data",
"data": {
"text/markdown": "```lisp\nt\n=> t```\n\n"
},
"metadata": {}
}
]
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "lisp\"'-123\"",
"execution_count": 21,
"outputs": [
{
"output_type": "display_data",
"data": {
"text/markdown": "```lisp\n'-123\n=> -123```\n\n"
},
"metadata": {}
}
]
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "lisp\"'nil\"",
"execution_count": 22,
"outputs": [
{
"output_type": "display_data",
"data": {
"text/markdown": "```lisp\n'nil\n=> nil```\n\n"
},
"metadata": {}
}
]
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "lisp\"'t\"",
"execution_count": 23,
"outputs": [
{
"output_type": "display_data",
"data": {
"text/markdown": "```lisp\n't\n=> t```\n\n"
},
"metadata": {}
}
]
},
{
"metadata": {
"scrolled": false,
"trusted": true
},
"cell_type": "code",
"source": "lisp\"(1 2 3)\"",
"execution_count": 24,
"outputs": [
{
"output_type": "error",
"ename": "LoadError",
"evalue": "Error: The car 1 of (1 2 3) is not callable.",
"traceback": [
"Error: The car 1 of (1 2 3) is not callable.",
"",
"Stacktrace:",
" [1] error(::String, ::String, ::String, ::String, ::String)",
" @ Base .\\error.jl:42",
" [2] eval_(sexpr::Tuple{Int64, Int64, Int64, Nil}, a::Nil)",
" @ Main .\\In[2]:79",
" [3] eval_(sexpr::Tuple{Int64, Int64, Int64, Nil})",
" @ Main .\\In[2]:62",
" [4] lisp(code::String)",
" @ Main .\\In[2]:150",
" [5] top-level scope",
" @ In[24]:1",
" [6] eval",
" @ .\\boot.jl:345 [inlined]",
" [7] include_string(mapexpr::typeof(REPL.softscope), mod::Module, code::String, filename::String)",
" @ Base .\\loading.jl:1019"
]
}
]
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "lisp\"'(1 2 3)\"",
"execution_count": 25,
"outputs": [
{
"output_type": "display_data",
"data": {
"text/markdown": "```lisp\n'(1 2 3)\n=> (1 2 3)```\n\n"
},
"metadata": {}
}
]
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "lisp\"(quote (1 2 3))\"",
"execution_count": 26,
"outputs": [
{
"output_type": "display_data",
"data": {
"text/markdown": "```lisp\n(quote (1 2 3))\n=> (1 2 3)```\n\n"
},
"metadata": {}
}
]
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "lisp\"(atom -123)\"",
"execution_count": 27,
"outputs": [
{
"output_type": "display_data",
"data": {
"text/markdown": "```lisp\n(atom -123)\n=> t```\n\n"
},
"metadata": {}
}
]
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "lisp\"(atom nil)\"",
"execution_count": 28,
"outputs": [
{
"output_type": "display_data",
"data": {
"text/markdown": "```lisp\n(atom nil)\n=> t```\n\n"
},
"metadata": {}
}
]
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "lisp\"(atom t)\"",
"execution_count": 29,
"outputs": [
{
"output_type": "display_data",
"data": {
"text/markdown": "```lisp\n(atom t)\n=> t```\n\n"
},
"metadata": {}
}
]
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "lisp\"(atom '(1 2 3))\"",
"execution_count": 30,
"outputs": [
{
"output_type": "display_data",
"data": {
"text/markdown": "```lisp\n(atom '(1 2 3))\n=> nil```\n\n"
},
"metadata": {}
}
]
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "lisp\"\"\"(eq 2 3)\"\"\"",
"execution_count": 31,
"outputs": [
{
"output_type": "display_data",
"data": {
"text/markdown": "```lisp\n(eq 2 3)\n=> nil```\n\n"
},
"metadata": {}
}
]
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "lisp\"\"\"(eq 3 3)\"\"\"",
"execution_count": 32,
"outputs": [
{
"output_type": "display_data",
"data": {
"text/markdown": "```lisp\n(eq 3 3)\n=> t```\n\n"
},
"metadata": {}
}
]
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "lisp\"(car '((1 2) (3 4) 5))\"",
"execution_count": 33,
"outputs": [
{
"output_type": "display_data",
"data": {
"text/markdown": "```lisp\n(car '((1 2) (3 4) 5))\n=> (1 2)```\n\n"
},
"metadata": {}
}
]
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "lisp\"(car (car '((1 2) (3 4))))\"",
"execution_count": 34,
"outputs": [
{
"output_type": "display_data",
"data": {
"text/markdown": "```lisp\n(car (car '((1 2) (3 4))))\n=> 1```\n\n"
},
"metadata": {}
}
]
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "lisp\"(cdr '((1 2) (3 4) 5))\"",
"execution_count": 35,
"outputs": [
{
"output_type": "display_data",
"data": {
"text/markdown": "```lisp\n(cdr '((1 2) (3 4) 5))\n=> ((3 4) 5)```\n\n"
},
"metadata": {}
}
]
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "lisp\"(car (cdr '((1 2) (3 4) 5)))\"",
"execution_count": 36,
"outputs": [
{
"output_type": "display_data",
"data": {
"text/markdown": "```lisp\n(car (cdr '((1 2) (3 4) 5)))\n=> (3 4)```\n\n"
},
"metadata": {}
}
]
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "lisp\"(cdr (cdr '((1 2) (3 4) 5)))\"",
"execution_count": 37,
"outputs": [
{
"output_type": "display_data",
"data": {
"text/markdown": "```lisp\n(cdr (cdr '((1 2) (3 4) 5)))\n=> (5)```\n\n"
},
"metadata": {}
}
]
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "lisp\"(cdr (cdr (cdr '((1 2) (3 4) 5))))\"",
"execution_count": 38,
"outputs": [
{
"output_type": "display_data",
"data": {
"text/markdown": "```lisp\n(cdr (cdr (cdr '((1 2) (3 4) 5))))\n=> nil```\n\n"
},
"metadata": {}
}
]
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "lisp\"(cdr (cdr (cdr (cdr '((1 2) (3 4) 5)))))\"",
"execution_count": 39,
"outputs": [
{
"output_type": "display_data",
"data": {
"text/markdown": "```lisp\n(cdr (cdr (cdr (cdr '((1 2) (3 4) 5)))))\n=> nil```\n\n"
},
"metadata": {}
}
]
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "lisp\"(cons 1 2)\"",
"execution_count": 40,
"outputs": [
{
"output_type": "display_data",
"data": {
"text/markdown": "```lisp\n(cons 1 2)\n=> (1 . 2)```\n\n"
},
"metadata": {}
}
]
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "lisp\"(cons 1 (cons 2 3))\"",
"execution_count": 41,
"outputs": [
{
"output_type": "display_data",
"data": {
"text/markdown": "```lisp\n(cons 1 (cons 2 3))\n=> (1 2 . 3)```\n\n"
},
"metadata": {}
}
]
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "lisp\"'(1 2 3)\"",
"execution_count": 42,
"outputs": [
{
"output_type": "display_data",
"data": {
"text/markdown": "```lisp\n'(1 2 3)\n=> (1 2 3)```\n\n"
},
"metadata": {}
}
]
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "lisp\"(cond ((eq 1 1) a) ((eq 1 2) b) ((eq 1 3) c) (t d))\"",
"execution_count": 43,
"outputs": [
{
"output_type": "display_data",
"data": {
"text/markdown": "```lisp\n(cond ((eq 1 1) a) ((eq 1 2) b) ((eq 1 3) c) (t d))\n=> :a```\n\n"
},
"metadata": {}
}
]
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "lisp\"(cond ((eq 2 1) a) ((eq 2 2) b) ((eq 2 3) c) (t d))\"",
"execution_count": 44,
"outputs": [
{
"output_type": "display_data",
"data": {
"text/markdown": "```lisp\n(cond ((eq 2 1) a) ((eq 2 2) b) ((eq 2 3) c) (t d))\n=> :b```\n\n"
},
"metadata": {}
}
]
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "lisp\"(cond ((eq 1 2) a) ((eq 3 2) b) ((eq 3 3) c) (t d))\"",
"execution_count": 45,
"outputs": [
{
"output_type": "display_data",
"data": {
"text/markdown": "```lisp\n(cond ((eq 1 2) a) ((eq 3 2) b) ((eq 3 3) c) (t d))\n=> :c```\n\n"
},
"metadata": {}
}
]
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "lisp\"(cond ((eq 0 1) a) ((eq 0 2) b) ((eq 0 3) c) (t d))\"",
"execution_count": 46,
"outputs": [
{
"output_type": "display_data",
"data": {
"text/markdown": "```lisp\n(cond ((eq 0 1) a) ((eq 0 2) b) ((eq 0 3) c) (t d))\n=> :d```\n\n"
},
"metadata": {}
}
]
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "lisp\"(foo '(1 2 3))\"",
"execution_count": 47,
"outputs": [
{
"output_type": "error",
"ename": "LoadError",
"evalue": "Error: The car :foo of (:foo (:quote (1 2 3))) is not callable.",
"traceback": [
"Error: The car :foo of (:foo (:quote (1 2 3))) is not callable.",
"",
"Stacktrace:",
" [1] error(::String, ::String, ::String, ::String, ::String)",
" @ Base .\\error.jl:42",
" [2] eval_(sexpr::Tuple{Symbol, Tuple{Symbol, Tuple{Int64, Int64, Int64, Nil}, Nil}, Nil}, a::Nil)",
" @ Main .\\In[2]:79",
" [3] eval_(sexpr::Tuple{Symbol, Tuple{Symbol, Tuple{Int64, Int64, Int64, Nil}, Nil}, Nil})",
" @ Main .\\In[2]:62",
" [4] lisp(code::String)",
" @ Main .\\In[2]:150",
" [5] top-level scope",
" @ In[47]:1",
" [6] eval",
" @ .\\boot.jl:345 [inlined]",
" [7] include_string(mapexpr::typeof(REPL.softscope), mod::Module, code::String, filename::String)",
" @ Base .\\loading.jl:1019"
]
}
]
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "lisp\"(cdr '(1 2 3))\"",
"execution_count": 48,
"outputs": [
{
"output_type": "display_data",
"data": {
"text/markdown": "```lisp\n(cdr '(1 2 3))\n=> (2 3)```\n\n"
},
"metadata": {}
}
]
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "lisp\"\"\"\n((lambda (f x) (f x)) 'cdr '(1 2 3))\n\"\"\"",
"execution_count": 49,
"outputs": [
{
"output_type": "display_data",
"data": {
"text/markdown": "```lisp\n((lambda (f x) (f x)) 'cdr '(1 2 3))\n=> (2 3)```\n\n"
},
"metadata": {}
}
]
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "lisp\"((lambda (x y) (car x)) '(1 2) 3)\"",
"execution_count": 50,
"outputs": [
{
"output_type": "display_data",
"data": {
"text/markdown": "```lisp\n((lambda (x y) (car x)) '(1 2) 3)\n=> 1```\n\n"
},
"metadata": {}
}
]
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "lisp\"((1 2) . (3 4))\"",
"execution_count": 51,
"outputs": [
{
"output_type": "error",
"ename": "LoadError",
"evalue": "Error: The car (1 2) of ((1 2) 3 4) is not callable.",
"traceback": [
"Error: The car (1 2) of ((1 2) 3 4) is not callable.",
"",
"Stacktrace:",
" [1] error(::String, ::String, ::String, ::String, ::String)",
" @ Base .\\error.jl:42",
" [2] eval_(sexpr::Tuple{Tuple{Int64, Int64, Nil}, Int64, Int64, Nil}, a::Nil)",
" @ Main .\\In[2]:79",
" [3] eval_(sexpr::Tuple{Tuple{Int64, Int64, Nil}, Int64, Int64, Nil})",
" @ Main .\\In[2]:62",
" [4] lisp(code::String)",
" @ Main .\\In[2]:150",
" [5] top-level scope",
" @ In[51]:1",
" [6] eval",
" @ .\\boot.jl:345 [inlined]",
" [7] include_string(mapexpr::typeof(REPL.softscope), mod::Module, code::String, filename::String)",
" @ Base .\\loading.jl:1019"
]
}
]
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "lisp\"'((1 2) . (3 4))\"",
"execution_count": 52,
"outputs": [
{
"output_type": "display_data",
"data": {
"text/markdown": "```lisp\n'((1 2) . (3 4))\n=> ((1 2) 3 4)```\n\n"
},
"metadata": {}
}
]
},
{
"metadata": {},
"cell_type": "markdown",
"source": "### 4つの例\n\nThe four examples in https://qiita.com/ytaki0801/items/f902bd81cc8828774911"
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "# example 1\nlisp\"(car (cdr '(10 20 30)))\"",
"execution_count": 53,
"outputs": [
{
"output_type": "display_data",
"data": {
"text/markdown": "```lisp\n(car (cdr '(10 20 30)))\n=> 20```\n\n"
},
"metadata": {}
}
]
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "# example 2\n\nlisp\"((lambda (x) (car (cdr x))) '(abc def ghi))\"",
"execution_count": 54,
"outputs": [
{
"output_type": "display_data",
"data": {
"text/markdown": "```lisp\n((lambda (x) (car (cdr x))) '(abc def ghi))\n=> :def```\n\n"
},
"metadata": {}
}
]
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "# example 3\n\nlisp\"\"\"\n((lambda (f x y) (f x (f y '())))\n '(lambda (x y) (cons x (cons y '())))\n '10 '20)\n\"\"\"",
"execution_count": 55,
"outputs": [
{
"output_type": "display_data",
"data": {
"text/markdown": "```lisp\n((lambda (f x y) (f x (f y '())))\n '(lambda (x y) (cons x (cons y '())))\n '10 '20)\n=> (10 (20 nil))```\n\n"
},
"metadata": {}
}
]
},
{
"metadata": {
"scrolled": false,
"trusted": true
},
"cell_type": "code",
"source": "# example 4\n\nlisp\"\"\"\n((lambda (assoc k v) (assoc k v))\n '(lambda (k v)\n (cond ((eq v '()) nil)\n ((eq (car (car v)) k)\n (car v))\n ('t (assoc k (cdr v)))))\n 'Orange\n '((Apple . 120) (Orange . 210) (Lemmon . 180)))\n\"\"\"",
"execution_count": 56,
"outputs": [
{
"output_type": "display_data",
"data": {
"text/markdown": "```lisp\n((lambda (assoc k v) (assoc k v))\n '(lambda (k v)\n (cond ((eq v '()) nil)\n ((eq (car (car v)) k)\n (car v))\n ('t (assoc k (cdr v)))))\n 'Orange\n '((Apple . 120) (Orange . 210) (Lemmon . 180)))\n=> (:Orange . 210)```\n\n"
},
"metadata": {}
}
]
},
{
"metadata": {},
"cell_type": "markdown",
"source": "1つ目のlambda式における変数 assoc には2つ目のlambda式が代入され, 2つ目のlambda式におけるassocとして1つ目のlambda式中のassoc が使われる. これによって, recursive call が実現されていることに注意せよ. Juliaで同様の結果を得るためのコードが次の節にある."
},
{
"metadata": {},
"cell_type": "markdown",
"source": "## Juliaにおける類似のコード\n\n直上の例は `assoc` の名前が有効な範囲が「広い」ことを巧妙に利用している.\n\nそれと同じことはJuliaでそのままではできないのだが, 以下のように極めて類似したコードを書くことができる(example4)."
},
{
"metadata": {},
"cell_type": "markdown",
"source": "このノートの実装では, Lispにおける (1 2 3 4) と (1 2 3 . 4) はJuliaにおけるタプルたち (1, 2, 3, 4, nil) と (1, 2, 3, 4) で実現されていることに注意せよ."
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "_sexpr(\"(1 2 3 4)\")",
"execution_count": 57,
"outputs": [
{
"output_type": "execute_result",
"execution_count": 57,
"data": {
"text/plain": "(1, 2, 3, 4, nil)"
},
"metadata": {}
}
]
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "sprint_sexpr((1, 2, 3, 4, nil)) ",
"execution_count": 58,
"outputs": [
{
"output_type": "execute_result",
"execution_count": 58,
"data": {
"text/plain": "\"(1 2 3 4)\""
},
"metadata": {}
}
]
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "_sexpr(\"(1 2 3 . 4)\")",
"execution_count": 59,
"outputs": [
{
"output_type": "execute_result",
"execution_count": 59,
"data": {
"text/plain": "(1, 2, 3, 4)"
},
"metadata": {}
}
]
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "sprint_sexpr((1, 2, 3, 4)) ",
"execution_count": 60,
"outputs": [
{
"output_type": "execute_result",
"execution_count": 60,
"data": {
"text/plain": "\"(1 2 3 . 4)\""
},
"metadata": {}
}
]
},
{
"metadata": {},
"cell_type": "markdown",
"source": "### example 1\n\n```lisp\n(car (cdr '(10 20 30)))\n=> 20\n```"
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "example1 = :(\n car(cdr((10, 20, 30, nil)))\n)\n\neval(example1)",
"execution_count": 61,
"outputs": [
{
"output_type": "execute_result",
"execution_count": 61,
"data": {
"text/plain": "20"
},
"metadata": {}
}
]
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "Meta.show_sexpr(Base.remove_linenums!(example1))",
"execution_count": 62,
"outputs": [
{
"output_type": "stream",
"text": "(:call, :car, (:call, :cdr, (:tuple, 10, 20, 30, :nil)))",
"name": "stdout"
}
]
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "using AbstractTrees\nAbstractTrees.printnode(io::IO, expr::Expr) = show(io, expr.head)\nprint_tree(Base.remove_linenums!(example1), 10)",
"execution_count": 63,
"outputs": [
{
"output_type": "stream",
"text": ":call\n├─ :car\n└─ :call\n ├─ :cdr\n └─ :tuple\n ├─ 10\n ├─ 20\n ├─ 30\n └─ :nil\n",
"name": "stdout"
}
]
},
{
"metadata": {},
"cell_type": "markdown",
"source": "### example2\n\n```lisp\n((lambda (x) (car (cdr x))) '(abc def ghi))\n=> :def\n```\n\nに類似のJuliaのコード:"
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "example2 = :(\n (function (x) car(cdr(x)) end)((:abc, :def, :ghi, nil))\n)\n\neval(example2)",
"execution_count": 64,
"outputs": [
{
"output_type": "execute_result",
"execution_count": 64,
"data": {
"text/plain": ":def"
},
"metadata": {}
}
]
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "Meta.show_sexpr(Base.remove_linenums!(example2))",
"execution_count": 65,
"outputs": [
{
"output_type": "stream",
"text": "(:call, (:function, (:tuple, :x), (:block,\n (:call, :car, (:call, :cdr, :x))\n )), (:tuple, (:quote, #QuoteNode\n :abc\n ), (:quote, #QuoteNode\n :def\n ), (:quote, #QuoteNode\n :ghi\n ), :nil))",
"name": "stdout"
}
]
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "using AbstractTrees\nAbstractTrees.printnode(io::IO, expr::Expr) = show(io, expr.head)\nprint_tree(Base.remove_linenums!(example2), 10)",
"execution_count": 66,
"outputs": [
{
"output_type": "stream",
"text": ":call\n├─ :function\n│ ├─ :tuple\n│ │ └─ :x\n│ └─ :block\n│ └─ :call\n│ ├─ :car\n│ └─ :call\n│ ├─ :cdr\n│ └─ :x\n└─ :tuple\n ├─ :(:abc)\n ├─ :(:def)\n ├─ :(:ghi)\n └─ :nil\n",
"name": "stdout"
}
]
},
{
"metadata": {},
"cell_type": "markdown",
"source": "### example3\n\n```lisp\n((lambda (f x y) (f x (f y '())))\n '(lambda (x y) (cons x (cons y '())))\n '10 '20)\n=> (10 (20 nil))\n```\n\nに類似のJuliaのコードの例."
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "example3 = :(\n (function (f, x, y) f(x, f(y, nil)) end)(\n (function (x, y) cons(x, cons(y, nil)) end),\n 10, 20\n )\n)\n\neval(example3) |> print_sexpr",
"execution_count": 67,
"outputs": [
{
"output_type": "stream",
"text": "(10 (20 nil))",
"name": "stdout"
}
]
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "Meta.show_sexpr(Base.remove_linenums!(example3))",
"execution_count": 68,
"outputs": [
{
"output_type": "stream",
"text": "(:call, (:function, (:tuple, :f, :x, :y), (:block,\n (:call, :f, :x, (:call, :f, :y, :nil))\n )), (:function, (:tuple, :x, :y), (:block,\n (:call, :cons, :x, (:call, :cons, :y, :nil))\n )), 10, 20)",
"name": "stdout"
}
]
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "using AbstractTrees\nAbstractTrees.printnode(io::IO, expr::Expr) = show(io, expr.head)\nprint_tree(Base.remove_linenums!(example3), 10)",
"execution_count": 69,
"outputs": [
{
"output_type": "stream",
"text": ":call\n├─ :function\n│ ├─ :tuple\n│ │ ├─ :f\n│ │ ├─ :x\n│ │ └─ :y\n│ └─ :block\n│ └─ :call\n│ ├─ :f\n│ ├─ :x\n│ └─ :call\n│ ├─ :f\n│ ├─ :y\n│ └─ :nil\n├─ :function\n│ ├─ :tuple\n│ │ ├─ :x\n│ │ └─ :y\n│ └─ :block\n│ └─ :call\n│ ├─ :cons\n│ ├─ :x\n│ └─ :call\n│ ├─ :cons\n│ ├─ :y\n│ └─ :nil\n├─ 10\n└─ 20\n",
"name": "stdout"
}
]
},
{
"metadata": {},
"cell_type": "markdown",
"source": "### example 4\n\n```lisp\n((lambda (assoc k v) (assoc k v))\n '(lambda (k v)\n (cond ((eq v '()) nil)\n ((eq (car (car v)) k)\n (car v))\n ('t (assoc k (cdr v)))))\n 'Orange\n '((Apple . 120) (Orange . 210) (Lemmon . 180)))\n=> (:Orange . 210)\n```\n\nに類似のJuliaのコード."
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "example4 = :(\n (\n function (assoc, k, v) assoc(k, v) end\n )(\n (\n function f(k, v)\n if v == ()\n nil\n elseif car(car(v)) == k\n car(v)\n else\n f(k, cdr(v))\n end\n end; f\n ),\n :Orange,\n ((:Apple, 120), (:Orange, 210), (:Lemmon, 180), nil)\n )\n)\n\neval(example4)",
"execution_count": 70,
"outputs": [
{
"output_type": "execute_result",
"execution_count": 70,
"data": {
"text/plain": "(:Orange, 210)"
},
"metadata": {}
}
]
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "Meta.show_sexpr(Base.remove_linenums!(copy(example4)))",
"execution_count": 71,
"outputs": [
{
"output_type": "stream",
"text": "(:call, (:function, (:tuple, :assoc, :k, :v), (:block,\n (:call, :assoc, :k, :v)\n )), (:block,\n (:function, (:call, :f, :k, :v), (:block,\n (:if, (:call, :(==), :v, (:tuple,)), (:block,\n :nil\n ), (:elseif, (:block,\n (:call, :(==), (:call, :car, (:call, :car, :v)), :k)\n ), (:block,\n (:call, :car, :v)\n ), (:block,\n (:call, :f, :k, (:call, :cdr, :v))\n )))\n )),\n :f\n ), (:quote, #QuoteNode\n :Orange\n ), (:tuple, (:tuple, (:quote, #QuoteNode\n :Apple\n ), 120), (:tuple, (:quote, #QuoteNode\n :Orange\n ), 210), (:tuple, (:quote, #QuoteNode\n :Lemmon\n ), 180), :nil))",
"name": "stdout"
}
]
},
{
"metadata": {
"trusted": true,
"scrolled": false
},
"cell_type": "code",
"source": "using AbstractTrees\nAbstractTrees.printnode(io::IO, expr::Expr) = show(io, expr.head)\nprint_tree(Base.remove_linenums!(copy(example4)), 10)",
"execution_count": 72,
"outputs": [
{
"output_type": "stream",
"text": ":call\n├─ :function\n│ ├─ :tuple\n│ │ ├─ :assoc\n│ │ ├─ :k\n│ │ └─ :v\n│ └─ :block\n│ └─ :call\n│ ├─ :assoc\n│ ├─ :k\n│ └─ :v\n├─ :block\n│ ├─ :function\n│ │ ├─ :call\n│ │ │ ├─ :f\n│ │ │ ├─ :k\n│ │ │ └─ :v\n│ │ └─ :block\n│ │ └─ :if\n│ │ ├─ :call\n│ │ │ ├─ :(==)\n│ │ │ ├─ :v\n│ │ │ └─ :tuple\n│ │ ├─ :block\n│ │ │ └─ :nil\n│ │ └─ :elseif\n│ │ ├─ :block\n│ │ │ └─ :call\n│ │ │ ├─ :(==)\n│ │ │ ├─ :call\n│ │ │ │ ├─ :car\n│ │ │ │ └─ :call\n│ │ │ │ ├─ :car\n│ │ │ │ └─ :v\n│ │ │ └─ :k\n│ │ ├─ :block\n│ │ │ └─ :call\n│ │ │ ├─ :car\n│ │ │ └─ :v\n│ │ └─ :block\n│ │ └─ :call\n│ │ ├─ :f\n│ │ ├─ :k\n│ │ └─ :call\n│ │ ├─ :cdr\n│ │ └─ :v\n│ └─ :f\n├─ :(:Orange)\n└─ :tuple\n ├─ :tuple\n │ ├─ :(:Apple)\n │ └─ 120\n ├─ :tuple\n │ ├─ :(:Orange)\n │ └─ 210\n ├─ :tuple\n │ ├─ :(:Lemmon)\n │ └─ 180\n └─ :nil\n",
"name": "stdout"
}
]
},
{
"metadata": {
"trusted": true
},
"cell_type": "code",
"source": "",
"execution_count": null,
"outputs": []
}
],
"metadata": {
"@webio": {
"lastKernelId": null,
"lastCommId": null
},
"_draft": {
"nbviewer_url": "https://gist.github.com/1702e7be9bc41321f0f2f2198b6ed7a3"
},
"gist": {
"id": "1702e7be9bc41321f0f2f2198b6ed7a3",
"data": {
"description": "Minimal Lisp in Julia - Part 1",
"public": true
}
},
"kernelspec": {
"name": "julia-1.6-o3-depwarn",
"display_name": "Julia 1.6.0-DEV depwarn -O3",
"language": "julia"
},
"language_info": {
"file_extension": ".jl",
"name": "julia",
"mimetype": "application/julia",
"version": "1.6.0"
},
"toc": {
"nav_menu": {},
"number_sections": true,
"sideBar": true,
"skip_h1_title": true,
"base_numbering": 1,
"title_cell": "Table of Contents",
"title_sidebar": "Contents",
"toc_cell": true,
"toc_position": {},
"toc_section_display": true,
"toc_window_display": false
}
},
"nbformat": 4,
"nbformat_minor": 4
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment