veer66

veer66

I specified the source path in $HOME/.config/common-lisp/source-registry.conf.

source-registry.conf:

(:source-registry
  (:tree (:home "Develop/t/mt2021"))
  (:tree (:home "Develop/free"))
  :inherit-configuration)

Common Lisp packages can be put in $HOME/Develop/t/mt2021 or Develop/free.

Now I create a new project using Quickproject.

In SBCL, I ran this:

* (ql:quickload 'quickproject)
* (quickproject:make-project #p"~/Develop/free/amphi" :depends-on '(arrow-macros cl-ppcre jonathan))

I just realized yesterday that I don't have to use

(asdf:load-system :amphi)

.

I can use

(ql:quickload :amphi)

to load my local projects. The upside of ql:quickload is that it also install all dependencies specified in .asd.

Now the process is easy and started to make sense. 😅

With the external-program package, I can code to call an external command line program easily. For example:

(external-program:run "ls" '("-l") :output *standard-output*)

What if I want to keep streaming (piping) input and reading output. (1) instead of run, I use start.

(setf *x* (external-program:start "cat" '() :output :stream :input :stream))

(2) I create a thread for reading the output and print it. Bordeaux-threads API looks quite similar to POSIX.

(setf *read-thread*
      (bordeaux-threads:make-thread
       (lambda ()
     (loop for line = (read-line (external-program:process-output-stream *x*) nil)
           while line
           do (format t "OUT: ~A~%" line)))
       :name "titi"))

(3) I get an output stream for passing data to the external program.

(setf *out* (external-program:process-input-stream *x*))

(4) I write something to out and flash it.

(progn
  (loop for i from 1 to 10
    do (format *out* "!!! ~A~%" i))
  (force-output *out*))

At this step, you should see some output from the (2) step, which are:

OUT: !!! 1
OUT: !!! 2
...
OUT: !!! 10

You can keep playing with it.

(5) And finally I clean everything up.

(close *out*)
(bordeaux-threads:join-thread *read-thread*)

I tried to use cl-lzma. I didn’t know that the vector should be static-vectors:vector instead of vector. Anyways, now I know.

(ql:quickload 'cl-lzma)
(use-package :cl-lzma)
(use-package :static-vectors)
(setq a (multiple-value-list 
                  (with-static-vector 
                              (v 100 :initial-contents (loop for i from 0 below 100 collect 8)) 
                    (lzma-compress v))))
(apply #'lzma-decompress a)

And, finally, it got

#(8 8 8 8 8 8 8 8 8 8 8
 8 8 8 8 8 8 8 8 8 8 8 8 
8 8 8 8 8 8 8 8 8 8 8 8 8
 8 8 8  8 8 8 8 8 8 8 8 8
 8 8 8 8 8 8 8 8 8 8 8 8 8 
8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8
 8 8 8 8 8 8 8 8 8 8 8 8 8 8
 8 8 8 8 8 8 8 8 8)

for decompression, which is correct!

Given I have a file of document id and text unit id. For example:

doc-id, text-unit-ids
1,100,200,300
2,50,8,1,6

I want to keep them in a list of doc-id, text-unit-id pairs. For example:

1,100
1,200
1,300
2,50
2,8
2,1
2,6

So I defined my variable in Rust, as follow:

let word_id_tu_ids: Vec<(u32, u32)> = vec![];

Sometimes I have a problem that I flipped them. I put a pair of text-unit-id and doc-id instead of text-unit-it and doc-id. For example, I put 100,1 instead of 100,1.

By (u32, u32), the Rust compiler cannot help me. So I tried:

type WordId = u32;
type DocId = u32;

fn push(w: WordId, d: DocId) {
    let mut v: Vec<(WordId, DocId)> = vec![];
    v.push((d, w));
}

It didn't help. So I tried struct instead.

struct WordId(u32);
struct DocId(u32);

fn push(w: WordId, d: DocId) {
    let mut v: Vec<(WordId, DocId)> = vec![];
    v.push((d, w));
}

Now the compiler can detect the error. However, maybe it is not clear to human eyes. So I defined another struct.

struct WordId(u32);
struct DocId(u32);

struct WordIdDocId {
    word_id: WordId,
    doc_id: DocId,
}

fn push(w: WordId, d: DocId) {
    let mut v: Vec<WordIdDocId> = vec![];
    v.push(WordIdDocId {word_id: w, doc_id: d});
}

Now it is clear to human eyes and the compiler. Anyways, is it what people call over-engineering? What if I want to:

let u = w + 1;

We can do it in Rust, but the code is going to be even longer. published: true description: tags: #rust #rustlang //coverimage: https://directurltoimage.jpg


Given I have a file of document id and text unit id. For example:

doc-id, text-unit-ids
1,100,200,300
2,50,8,1,6

I want to keep them in a list of doc-id, text-unit-id pairs. For example:

1,100
1,200
1,300
2,50
2,8
2,1
2,6

So I defined my variable in Rust, as follow:

let word_id_tu_ids: Vec<(u32, u32)> = vec![];

Sometimes I have a problem that I flipped them. I put a pair of text-unit-id and doc-id instead of text-unit-it and doc-id. For example, I put 100,1 instead of 100,1.

By (u32, u32), the Rust compiler cannot help me. So I tried:

type WordId = u32;
type DocId = u32;

fn push(w: WordId, d: DocId) {
    let mut v: Vec<(WordId, DocId)> = vec![];
    v.push((d, w));
}

It didn't help. So I tried struct instead.

struct WordId(u32);
struct DocId(u32);

fn push(w: WordId, d: DocId) {
    let mut v: Vec<(WordId, DocId)> = vec![];
    v.push((d, w));
}

Now the compiler can detect the error. However, maybe it is not clear to human eyes. So I defined another struct.

struct WordId(u32);
struct DocId(u32);

struct WordIdDocId {
    word_id: WordId,
    doc_id: DocId,
}

fn push(w: WordId, d: DocId) {
    let mut v: Vec<WordIdDocId> = vec![];
    v.push(WordIdDocId {word_id: w, doc_id: d});
}

Now it is clear to human eyes and the compiler. Anyways, is it what people call over-engineering? What if I want to:

let u = w + 1;

We can do it in Rust, but the code is going to be even longer.

Prerequisite

Emacs

(setq inferior-lisp-program "sbcl")

Anyways, for me, I need more memory so I did this.

(setq inferior-lisp-program "sbcl --dynamic-space-size 13000")

ASDF

  • Edit ~/.config/common-lisp/source-registry.conf
(:source-registry
  (:tree (:home "Develop/thesis"))
  :inherit-configuration)

Develop/thesis must be changed to a path to a directory in the home directory.

The project

My project name is mt-seq and I put it in ~/Develop/thesis.

Update 2020-12-06: Or the project can be created using quickproject. Thanks Michał “phoe” Herda.

  • ~/Develop/thesis/mt-seq.asd
(defsystem mt-seq
  :description "mt-seq"
  :author "Vee Satayamas"
  :license "LLGPL"
  :depends-on ("asdf" "lparallel" "bt-semaphore")
  :components ((:module "src"
		:serial t
		:components ((:file "packages")
			     (:file "mt")))))
  • ~/Develop/thesis/src/mt.lisp
(in-package :mt-seq)

(defun toto (x)
  x)
  • ~/Develop/thesis/src/packages.lisp
(defpackage :mt-seq
  (:use :cl :lparallel :bt-semaphore)
  (:export #:toto))

I expect that this should be sufficient for starting a new project in Common Lisp in a proper format.

เขียน #rustlang แบบ

(word_id, textunit_id) 

วันเดียวพอจำได้หรอก เขียนไปนาน ๆ อาจจะพลาดใส่

(textunit_id, word_id)

ก็ได้

อันนี้ static type อาจจะวืดเพราะทั้ง textunit-id และ word-id ก็เป็น u32 ทั้งคู่ อาจจะแก้แบบนี้สร้าง type มาใหม่เลย WordId กับ TextunitId แต่มันก็เหนื่อยอยู่นะ

ไม่ก็ใช้ struct แทน เช่น

struct WordIdTextunitId {
    word_id: u32,
    textunit_id: u32,
}

อันนี้เขียนง่ายดี แต่ compiler ไม่ได้ช่วยอะไรมากนะ ก็คือ programmer ก็ดูเอาเมื่อไหร่เขียน

WordIdTextunitId { word_id: textunit_id, textunit_id: word_id }

แบบนี้ก็ผิด โปรแกรมเมอร์เห็นเอง แต่ใส่สลับกัน compiler มันก็ผ่านนะ

แล้วคิดไปติดมามันก็พอกับเขียน Clojure เลย

{:word-id word-id
 :textunit-id textunit-id}

แบบนี้ก็ชัดเจนอยู่แล้ว ไม่ต้องประกาศ struct ด้วย หรือจะใช้ defstruct เลยก็ได้

ใน Common Lisp ก็คล้าย ๆ กัน def struct ก็ได้ จะทำเป็น alist ก็ได้

(list (cons :word-id word-id) 
       (cons :textunit-id textunit-id))

แบบนี้ก็ได้

กลับมา Rust จะจัดหนักแบบ ทำแบบนี้ก็บึ้มอยู่ดี

type WordId = u32;
type TextunitId = u32;

struct WordIdTextUnitId {
    word_id: WordId,
    textunit_id: TextunitId,
}

fn main() {
   let w: WordId = 1;
   let t: TextunitId = 2;
   let x = WordIdTextUnitId { word_id: t, textunit_id: w};
}

สลับได้ compiler ไม่ช่วยอะไร

แต่ถ้าทำเป็น struct หมดเลย

struct WordId(u32);
struct TextunitId(u32);

struct WordIdTextUnitId {
    word_id: WordId,
    textunit_id: TextunitId,
}

fn main() {
    let w = WordId(1);
    let t = TextunitId(2);
    let x = WordIdTextUnitId { word_id: t, textunit_id: w};
}

แบบนี้ compiler มันจะช่วยได้รู้แล้วว่าสลับกัน

แต่ก็จะมาเจอว่าผมอยากได้

let v = w + 1; 

แบบนี้ทำไง ก็มีวิธีทำหลายทางนะ เพียงแต่มันก็ต้องออกแรง

ผมจะเลือกจากข้อมูลตามหน้านี้ของ Wikipedia โดยที่เลือกสิ่งที่สนใจตามแบบผมเอง

Wikipedia's table

ข้อแรกผมต้องการแอปที่มีหลากหลาย platform ที่สุด

Android ได้ Linux ได้ Web ได้ iOS ได้ Windows ได้ macOS ได้ เพื่อที่ว่าจะได้มีคนถูกกีดกันออกไปน้อยที่สุด แต่ถ้าอยากเน้นกีดกันก็เลือก Messages ของ Apple เลย ใครไม่ชอบ Apple ไม่ใช้ Apple ก็ไม่ต้องมาคุย

มีเลือกแบบนี้พวก Whatsapp, Facebook Messenger, Line, Signal ก็ตกรอบไป

แอปที่ผ่านเข้ารอบมีตามนี้ Discord Gadu-Gadu ICQ Jitsi Mattermost Movim Element Skype Slack Telegram Trillian Wire

ข้อสองสองตัดพวกใช้เบอร์โทรออก

เพราะถ้าคิดว่าจะถูกคุกตาม นี่เบอร์โทรลงทะเบียนบัตรประชาชนไว้ น่าจะหาตัวได้

ดังนั้นก็จะเหลือ Discord Gadu-Gadu Jitsi Mattermost Movim Element Skype Slack Trillian Wire

ข้อสามเพื่อที่ว่าจะเชื่อถือได้โปรแกรมมันก็ควรจะ open source เวลาเข้ารหัสก็เปิดดูได้ว่าเข้าใจไหม ทำอย่างไร

ก็จะเหลือ Jitsi Mattermost Movim Element Wire

ข้อที่สี่ต้องเข้ารหัสจากผู้ส่งถึงผู้รับโดยตรงก็เหลือ

Element กับ Wire

ข้อที่ห้าต้องเข้ารหัสตลอดเวลาก็เหลือ

Wire ตัวเดียว

  • sudo apt-get install fonts-noto fonts-noto-extra fonts-noto-core fonts-noto-mono fonts-noto-ui-core fonts-noto-ui-extra fonts-noto-cjk fonts-noto-unhinted fonts-noto-hinted fonts-noto-cjk-extra fonts-noto-color-emoji
  • mkdir -p $HOME/.config/fontconfig
  • cat > $HOME/.config/fontconfig/fonts.conf

แปะอันนี้ใส่ไป

<?xml version='1.0'?>
<!DOCTYPE fontconfig SYSTEM 'fonts.dtd'>
<fontconfig>
  <match>
    <test compare="contains" name="lang">
      <string>th</string>
    </test>
    <edit mode="prepend" name="family">
      <string>Noto Sans Thai</string>
    </edit>
  </match>
  <alias>
    <family>serif</family>
    <prefer>
      <family>Noto Serif</family>
    </prefer>    
  </alias>
  <alias>
    <family>sans-serif</family>
    <prefer>
      <family>Noto Sans</family>
    </prefer>    
  </alias>
  <alias>
    <family>sans</family>
    <prefer>
      <family>Noto Sans</family>
    </prefer>    
  </alias>
  <alias>
    <family>monospace</family>
    <prefer>
      <family>Noto Mono</family>
    </prefer>    
  </alias>
</fontconfig>
  • กด control D

เสร็จ

บางทีเปลี่ยน font แล้วลง พวก Noto เรียนร้อยแล้วก็ยังไม่ได้อย่างใจ บางทีเป็นเพราะว่าระบบจะเลือก font ให้เราเอง ซึ่งไปแก้ได้ในไฟล์

ผมชึ้นหัวว่า MX Linux แต่จริง ๆ จะใช้ Void Linux, Ubuntu กระทั่งพวก NetBSD ก็น่าจะเหมือนกันหมด

$HOME/.config/fontconfig/fonts.conf

ซึ่งอันนี้ผมแก้ตาม https://jichu4n.com/posts/how-to-set-default-fonts-and-font-aliases-on-linux/ และ https://wiki.archlinux.org/index.php/Font_configuration/Examples

สมมุติว่าในกรณีที่ลง Noto แต่ไม่มี font ของ TLWG นะครับ ใส่ไปแบบนี้

<?xml version='1.0'?>
<!DOCTYPE fontconfig SYSTEM 'fonts.dtd'>
<fontconfig>
  <match>
    <test compare="contains" name="lang">
      <string>th</string>
    </test>
    <edit mode="prepend" name="family">
      <string>Noto Sans Thai</string>
    </edit>
  </match>
  <alias>
    <family>serif</family>
    <prefer>
      <family>Noto Serif</family>
    </prefer>    
  </alias>
  <alias>
    <family>sans-serif</family>
    <prefer>
      <family>Noto Sans</family>
    </prefer>    
  </alias>
  <alias>
    <family>sans</family>
    <prefer>
      <family>Noto Sans</family>
    </prefer>    
  </alias>
  <alias>
    <family>monospace</family>
    <prefer>
      <family>Noto Mono</family>
    </prefer>    
  </alias>
</fontconfig>

แต่ทำไปทำมาก็รู้สึกว่าไม่ค่อยโดยเท่าไหร่ ผมก็ลง Loma แล้วเปลี่ยน Noto Sans Thai เป็น Loma อยู่ดี

ชีวิตผมจะทำอะไรอีก ก็มีประมาณนี้ล่ะ

(defn iter>seq [it]
  []
  (when (.hasNext it)
    (let [p (.next it)]
      (lazy-seq (cons [(read-val* (.key p))
                       (read-val (.val p))]
                      (iter>seq it))))))