12 May 2022

Vee

Trying TruffleRuby

I ran wbrk2.rb:

# encoding: UTF-8
require 'thailang4r/word_breaker'
word_breaker = ThaiLang::WordBreaker.new
line = 'แต่ผมเคยมีปัญหาตอนจ่ายตังหลายปีแล้วแต่ผมเคยมีปัญหาตอนจ่ายตังหลายปีแล้วแต่ผมเคยมีปัญหาตอนจ่ายตังหลายปีแล้วแต่ผมเคยมีปัญหาตอนจ่ายตังหลายปีแล้วแต่ผมเคยมีปัญหาตอนจ่ายตังหลายปีแล้วแต่ผมเคยมีปัญหาตอนจ่ายตังหลายปีแล้วแต่ผมเคยมีปัญหาตอนจ่ายตังหลายปีแล้วแต่ผมเคยมีปัญหาตอนจ่ายตังหลายปีแล้วแต่ผมเคยมีปัญหาตอนจ่ายตังหลายปีแล้ว'

t1 = Time.now
for i in 1..10000
  puts word_breaker.break_into_words(line).join("|")
end
t2 = Time.now
puts t2-t1

in cRuby, jRUby, TruffleRuby by this script:

#!/bin/bash
IMPLS=("cruby1" "jruby1" "truffle1")
for i in $(seq 1 10); do
    for j in ${IMPLS[*]}; do
    echo $i,$j,$(docker run --rm -it -v $(pwd):/work -w /work $j ruby wbrk2.rb | tail -n1)
    done
done

Dockerfile.cruby:

FROM ruby:3.1
RUN gem install thailang4r

Dockerfile.jruby:

FROM jruby
RUN gem install thailang4r

Dockerfile.truffle:

FROM ghcr.io/graalvm/truffleruby:22
RUN gem install thailang4r
cRuby jRuby TruffleRuby
1 16.42368491 54.169815 11.385601
2 16.58381034 51.668375 11.99536
3 16.47655229 53.584401 12.088978
4 17.52281777 54.222869 10.893925
5 16.92775603 54.912019 12.24505
6 16.89063941 52.43011 11.413859
7 17.12419062 57.041267 11.395317
8 17.51911099 51.571193 11.551416
9 16.97416603 51.805853 11.578309
10 16.79307381 51.006189 12.517783
mean 16.92358022 53.2412091 11.7065598
Speed 1X 0.32X 1.45X

wbrk2 on TruffleRuby ran at 1.45X speed of cRuby. wbrk2 on jRuby ran at 0.32X speed of cRuby.

โดย Vee Satayamas ณ 12 May 2022 12:37 +0000

11 May 2022

Vee

Fortune telling

I was in a world where a code describing type signature was longer than a code telling what a program does. I don't want to bring that situation back.

I expect AI code completion to replace static typing. Ruby Type Signature (RBS) and similar things must be inferred automatically from a database or other data sources.

โดย Vee Satayamas ณ 11 May 2022 06:10 +0000

10 May 2022

Vee

C-style for loop in Common Lisp's Loop macro

I try to convert C-style for-loop in JS to Common Lisp's loop macro for a learning purpose.

for (i=0; i<10; i++) {
  console.log(i)
}

Above JS code can be converted to:

(loop with i = 0
      unless (< i 10) return nil
      do (print i)
      do (incf i))

And, of course, the version below is more appropriate.

(loop for i from 0 below 10 do
    (print i))

The first version looks more flexible in applying to another problem than the last one.

โดย Vee Satayamas ณ 10 May 2022 18:20 +0000

bact

จำกัดความเร็ว-เพิ่มความปลอดภัย: การใช้การออกแบบและเทคโนโลยีมาช่วยให้คนทำตามกฎได้สะดวกขึ้น

ป้ายเตือนหน้ามหาวิทยาลัยทรินิตีคอลเลจดับลิน แจ้งให้รถใช้ความเร็วไม่เกิน 30 กม./ชม.

เมื่อต้นปีนี้ ไปเที่ยวนอกเมืองตามเส้นทาง Wild Atlantic Way กับเพื่อนๆ ที่ดับลิน รถที่เช่าเป็น Volkswagen Golf ซึ่งระหว่างขับจะแสดงตัวเลขขีดจำกัดความเร็วของถนนช่วงนั้นขึ้นบนหน้าปัดเลย

ถ้าเกินขีดจำกัด ตัวเลขจะเปลี่ยนเป็นสีเหลือง
เกินนานๆ จะมีเสียงร้องเตือน
ถ้าเกินขีดไปมาก ตัวเลขจะเปลี่ยนเป็นสีแดง และยิ่งร้องดังหนัก

บนหน้าปัดยังมีกราฟิกแสดงระยะห่างจากรถคันหน้าที่เหมาะสมกับความเร็วที่ใช้อยู่ในปัจจุบันด้วย โดยจะแสดงเป็นรูปรถและเส้นแสดงระยะ

นอกจากกฎที่เหมาะสมแล้ว เทคโนโลยี + การออกแบบก็ช่วยให้คนตัดสินใจทำตามกฎได้สะดวกขึ้น

รถยี่ห้ออื่นก็มีความสามารถเดียวกันนี้ ลองค้นในเน็ต เจอคู่มือภาษาไทยของ Volvo S90 รุ่นปี 2020 เรื่อง “การ​เตือน​สำหรับ​ขีดจำกัด​ความเร็ว​และ​กล้อง​ตรวจจับ​ความเร็ว​จาก​ระบบ​ข้อมูล​ป้ายถนน” ด้วย

ตัวอย่าง​​ตัว​จำกัด​ความเร็ว​บน​จอแสดงผล​สำหรับคนขับ ในรถวอลโว่ S90 (2020) จากเว็บไซต์ volvocars.com

การกำหนดขีดจำกัดความเร็วตามลักษณะการใช้สอยพื้นที่

โดยทั่วไป ความเร็วสูงสุดในเขตเมืองดับลิน (Dublin City Council – ประมาณเทศบาลนครของไทย) คือ 50 กม./ชม. — ส่วนย่านที่อยู่อาศัย ย่านชุมชน จะกำหนดที่ไม่เกิน 30 กม./ชม.

ยกเว้นทางหลวงในเขตเมืองซึ่งถนนแยกเป็นสัดส่วนเฉพาะ จะอยู่ที่ 60 หรือ 80 กม./ชม. — ถ้าพ้นเขตเมืองไปแล้วก็จะสูงกว่านี้

เว็บไซต์รัฐบาลท้องถิ่นมีแผนที่ข้อเสนอการจำกัดความเร็วให้ดาวน์โหลด

สีชมพูคือพื้นที่ที่ปัจจุบันจำกัดความเร็วที่ 30 กม./ชม. ส่วนพื้นที่สีเหลือง ฟ้า และแดง กำลังถูกเสนอให้จำกัดความที่ 30 กม./ชม. เช่นกัน จะเห็นได้ว่าตัวเมืองเองพยายามจะขยายเขตที่เป็น 30 กม./ชม. ออกไปให้มากขึ้น โดยในเว็บไซต์บอกว่ารัฐบาลท้องถิ่นตั้งใจจะกำหนดให้ทุกย่านที่อยู่อาศัย ใช้ความเร็วไม่เกิน 30 กม./ชม. ภายในสิ้นปีที่ผ่านมา

แผนที่แสดงเขตจำกัดความเร็วที่มีอยู่เดิมและที่เสนอใหม่ ในบริเวณนครดับลิน

เทคโนโลยีที่ช่วยให้ขับรถปลอดภัยขึ้น

สหภาพยุโรปออกกฎความปลอดภัยทั่วไปฉบับปรับปรุง (Regulation 2019/2144 หรือ “General Safety Regulation 2” หรือ “GSR2”) เมื่อ 27 พ.ย. 2562 กำหนดให้ตั้งแต่วันที่ 6 ก.ค. 2565 (ปีนี้) รถในประเภท M1 (รถยนต์นั่ง) และ N1 (รถเพื่อการพาณิชย์ขนาดเบา) รุ่นใหม่ทุกรุ่นที่จะขออนุมัติเพื่อขายในยุโรป ต้องติดตั้งเทคโนโลยีเพื่อความปลอดภัย เช่น ระบบช่วยเตือนเมื่อใช้ความเร็วเกินกำหนด (intelligent speed assistance – ISA) และเทคโนโลยีช่วยในกรณีฉุกเฉินให้ขับอยู่ในช่องจราจร (emergency lane keeping systems – ELKS) ซึ่งประกอบไปด้วยเทคโนโลยีอีกย่อย 2 อย่างประกอบกันคือ การเตือนเมื่อวิ่งคร่อมช่องจราจร (lane departure warning systems – LDWS) และ การช่วยขยับให้กลับช่องจราจรที่ถูกต้อง (corrective directional control functions – CDCF)

ทั้งนี้ภายใน 7 ก.ค. 2567 รถใหม่ทุกคัน ไม่ว่าจะรุ่นใดก็ตาม จะต้องมีเทคโนโลยีความปลอดภัยต่างๆ ข้างต้น

นับจากวันที่ประกาศกฎหมายจนถึงวันบังคับใช้ขยักแรก (เฉพาะรถรุ่นใหม่) 2 ปี 7 เดือน และขยักสอง (รถใหม่ทุกคัน) 4 ปี 7 เดือน

เทคโนโลยีช่วยควบคุมความเร็วอัจฉริยะทำงานอย่างไร

วิดีโออธิบายการทำงานของระบบช่วยเตือนขีดจำกัดความเร็ว โดย Road Safety Authority Ireland

ที่หน้ารถจะมีกล้องที่อ่านป้ายแจ้งขีดจำกัดความเร็วที่ติดไว้ข้างถนน (ตัวป้ายมีมาตรฐานกำหนดหน้าตาไว้) ไม่จำเป็นต้องเชื่อมกับ GPS หรืออินเทอร์เน็ต

นอกจากนี้ยังสามารถใช้แผนที่ดิจิทัลที่มีข้อมูลขีดจำกัดความเร็วของถนนแต่ละช่วง (ข้อมูลน่าจะมาจากหน่วยงานรัฐที่ดูแลเรื่องถนน) + ข้อมูลที่ตั้งของรถจาก GPS เสริมด้วย เผื่ออ่านป้ายไม่ได้ (ป้ายมีฝุ่น ถูกต้นไม้บัง กล้องถูกบัง มีหมอก ฯลฯ)

ระบบนี้จะถูกเปิดโดยอัตโนมัติเป็นค่าปริยายเมื่อจุดเครื่อง และผู้ขับสามารถเลือกกดปิดการทำงานได้

ตัวอย่าง​ของ​ป้าย​จราจร​ที่​รถ Volvo รุ่น S90 (2020) สามารถ​อ่านได้

ตัวอย่าง​ของ​ป้าย​จราจร​ที่​รถวอลโว่รุ่น S90 (2020) สามารถ​อ่านได้
ตัวอย่าง​ของ​ป้าย​จราจร​ที่​รถวอลโว่รุ่น S90 (2020) สามารถ​อ่านได้

กลไกตลาด กับ การส่งเสริมด้วยกฎหมาย

เมื่อต้องควักกระเป๋าจ่ายเอง และเงินในกระเป๋ามีจำกัด คนมีแนวโน้มเลือกซื้อความปลอดภัยสำหรับตัวเองก่อน เช่น ถุงลมนิรภัย (คุ้มครองคนในรถ) ระบบกันเบรกล็อก (ช่วยมากกรณีใช้ความเร็วสูง ซึ่งคนในรถเสี่ยงจะเจ็บด้วย)

ในขณะที่เทคโนโลยีเพื่อความปลอดภัยของคนอื่น เช่น เทคโนโลยีที่ช่วยลดโอกาสชนที่ความเร็วต่ำ (ที่คนในรถมักไม่เจ็บ แต่คนนอกรถเจ็บ) หรือเทคโนโลยีที่ช่วยให้คนขับรถบรรทุก รถลาก สามารถมองเห็นจักรยานหรือรถเล็กในจุดบอดสายตา จะถูกซื้อทีหลัง หรือถูกซื้อน้อยกว่า

การใช้ “กลไกตลาด” เพียงอย่างเดียวจึงอาจไม่มากพอหรือไม่เร็วพอ

การเพิ่มความปลอดภัยบนท้องถนนด้วยการออกเป็นกฎหมายให้รถยนต์ใหม่ต้องมีเทคโนโลยีความปลอดภัยที่คำนึงถึงความปลอดภัยของทุกคนบนถนนก็เป็นอีกแนวทางหนึ่งที่น่าพิจารณา โดยคำนึงถึงผลกระทบและภาระของผู้ซื้อรถหรือใช้รถด้วย อาจจะค่อยเป็นค่อยไปอย่างกรณีของสหภาพยุโรป ที่มาการออกกฎหมายล่วงหน้าหลายปี และเริ่มบังคับกับรถรุ่นใหม่ก่อน รถใหม่ที่ยังเป็นรุ่นเก่าอยู่ยังไม่ต้อง และให้เวลาอีก 2 ปี จึงจะขยับไปสู่รถใหม่ทุกคัน

ใครสนใจเรื่องเทคโนโลยีช่วยความปลอดภัยเชิงรุก (“active”) เพื่อป้องกันไม่ให้เกิดอุบัติเหตุแต่แรก [เทียบกับเทคโนโลยี “passive” เช่น ถุงลมนิรภัย ที่เป็นเรื่องลดความเสียหายเมื่อเกิดเหตุแล้ว] ดูได้เว็บไซต์ RoadSafetyFacts.eu — ซึ่งจะเป็นข้อมูลจากมุมมองของสมาคมผู้ผลิตรถยนต์ European Automobile Manufacturers’ Association (ACEA)

ระบบความปลอดภัยเชิงรุกของรถยนต์

สำหรับข้อมูลเรื่องความปลอดภัยบนท้องถนนในสหภาพยุโรป ดูได้ที่เว็บไซต์ของ European Transportation Safety Council ซึ่งเป็นองค์กรไม่แสวงกำไร ได้รับเงินสนับสนุนจากทั้งภาครัฐที่เป็นผู้กำกับกิจการ และเอกชน ซึ่งรวมถึงผู้ผลิตรถยนต์และสมาคมผู้ผลิตสุรา จะมีข่าวสารเกี่ยวกับพวกกฎหมายใหม่ๆ ด้วย

เผยแพร่ต้นฉบับครั้งแรกเมื่อ 21 ก.พ. 2565 ในเฟซบุ๊ก

โดย bact ณ 10 May 2022 04:46 +0000

9 May 2022

Vee

Compile-time type checking in Common Lisp: a small experiment

In static1.lisp:

(defpackage static1 
 (:use #:cl)) 
(in-package :static1) 

(declaim (ftype (function (string string) string) concat-strings)) 
(defun concat-strings (a b)
  (format nil "~A --- ~A" a b)) 

(declaim (ftype (function (fixnum fixnum) string) concat-nums)) 
(defun concat-nums (a b)
  (concat-strings a b))

Then I ran this command:

sbcl --noinform --eval '(compile-file "static1.lisp")' --quit

SBCL showed this warning:

; caught WARNING: 
;  Derived type of STATIC1::A is 
;    (VALUES FIXNUM &OPTIONAL), 
;  conflicting with its asserted type 
;    STRING. 
;  See also: 
;    The SBCL Manual, Node "Handling of Types"

So SBCL - a Common Lisp implementation, can check type in compile-time. Anyway, a programmer needs to read warnings.

โดย Vee Satayamas ณ 9 May 2022 08:04 +0000

25 April 2022

Vee

Less visual-centric programming languages

Last week, I watched a video about coding and Docker configuration on a TV. I couldn't read any line of code. Then I thought about visually impaired people. How do they code? Every student in Thailand must learn to code. I presume the situation is similar in every country.

People widely use text-to-speech services these days. I cannot find any text-to-speech service for reading source code aloud. Let's assume we have a modified version of a text-to-speech service.

So I looked at source codes in different programming languages on the CodeRosetta website. I perceive Python code blocks by their visual structure purely. To read Python source code, I have to encode its visual structure, namely indents to sounds. Reading a nested code block won't be easy to understand. For example, reading twelve leading white spaces aloud will be very strange. In Lisp, reading open parenthesis and close parenthesis is more straightforward, but I will forget which parenthesis. So the best form of code blocks is in QuickBasic, which has different keywords between different kinds of blocks. For example, FOR with NEXT, and IF with END IF. Later I got a comment from Lemmy.ml, which told me that Ada also has different keywords between different kinds of blocks. Another idea from Lemmy.ml is the reader must convert the Python code block into a similar form as Ada or QuickBasic before reading.

MBasic refers to code by line numbers instead of code blocks. However, by listening to five lines, I forgot the line number. For example, when I heard gosub 70, I forgot what was at line 70.

In X86 Assembly, a programmer labels only the line that the program will jump to it. So X86 Assembly code looks much better than MBasic.

Still, coding in X86 Assembly can be exhaustive in many cases. For example, X86 Assembly doesn’t support recursion. Writing quick sort in X86 Assembly can be too difficult for learning to code.

Haskell doesn’t rely on code blocks. However, reading the symbols, for example, >>= is challenging. Prolog’s symbols are easy to read. For example, we can read :- as IF. Anyway, the Prolog programming paradigm is different from the mainstream one now. So Erlang, whose syntax is similar to Prolog, is a more practical alternative.

In brief, Erlang is a practical, less visual-centric program language. Because it mostly relies on names instead of code blocks, reading names aloud is much easier than reading code blocks aloud. Furthermore, the Erlang programming paradigm is more mainstream now.

โดย Vee Satayamas ณ 25 April 2022 09:38 +0000

20 April 2022

Vee

Clojure's lazy sequence versus transducer benchmark

According to this thread, I compared using only lazy sequences with transducers.

To add the I/O factor, I prepared a data file called "fake.txt" using the program below:

(with-open [w (io/writer "fake.txt")]
  (doseq [n (range 10000000)]
    (.write w (str n "\n"))))

F1 is the lazy-sequence-based version. It reads data from "fake.txt" and does a few steps of computations.

(defn f1
  []
  (with-open [r (io/reader "fake.txt")]
    (->> (line-seq r)
         (map parse-long)
         (map inc)
         (filter even?)
         (map inc)
         (reduce + 0))))

F2 is the transducer-based version of F1.

(defn f2
  []
  (with-open [r (io/reader "fake.txt")]
    (transduce (comp (map parse-long)
                     (map inc)
                     (filter even?)
                     (map inc))
               +
               (line-seq r))))

I evaluated them using Criterium.

(with-progress-reporting (quick-bench (f1) :verbose))
(with-progress-reporting (quick-bench (f2) :verbose))

Here is the result.

#################### F1 ###################
Evaluation count : 6 in 6 samples of 1 calls.
Execution time sample mean : 3.811858 sec
Execution time mean : 3.812064 sec

#################### F2 ###################
Evaluation count : 6 in 6 samples of 1 calls.
Execution time sample mean : 1.490624 sec
Execution time mean : 1.490777 sec

F1, which is the lazy sequence version, took 3.812064 seconds. F2, which is the transducer version, took 1.490777. So the transducer version is 155.71% faster than the lazy sequence version.

In brief, this biased experiment shows the transducer version is much faster than the pure lazy sequence version.

โดย Vee Satayamas ณ 20 April 2022 06:42 +0000

12 April 2022

Kitt

วันสงกรานต์ปี 2565

สงกรานต์ปี 2565 เป็นปี จ.ศ. (2565 – 1181) = 1384 วันเถลิงศก ตรงกับ(1384 * 0.25875)+ floor(1384 / 100 + 0.38)– floor(1384 / 4 + 0.5)– floor(1384 / 400 + 0.595)– 5.53375= 358.11 + 14 – 346 – 4 – 5.53375= 16.57625= วันที่ 16 เมษายน 2565 เวลา 13:49:48 วันสงกรานต์ ตรงกับ16.57625 – 2.165 = 14.41125= วันที่ 14 เมษายน 2565 … Continue reading วันสงกรานต์ปี 2565

โดย kitty ณ 12 April 2022 11:14 +0000

23 March 2022

Vee

I recommend Clojure as the first programming language to learn for beginners.

I recommend Clojure as the first programming language to learn for beginners. I recommend "Poetry of Programming" as the first book for learning Clojure. I recommend "Clojure for the Brave and True" as the second book for learning Clojure.

โดย Vee Satayamas ณ 23 March 2022 06:13 +0000

22 March 2022

Vee

สองเหตุผลที่ฉันคิดว่า Lisp น่าสนใจ (เวอร์ชั่นพ.ศ. 2565)

  1. Lisp สอนให้ฉันสร้างฟังก์ชันที่แปลงข้อมูลทีละขั้นตอน แทนที่จะเพิ่มตัวแปรที่ถูกแก้ไขไปเรื่อย ๆ ลงในลูป โครงสร้างข้อมูลที่ไม่ถูกเปลี่ยนค่ามีความสำคัญสำหรับการแปลงข้อมูลทีละขั้นตอนโดยไม่ทำให้ข้อมูลที่มีอยู่แล้วยุ่งเหยิง โครงสร้างข้อมูลเริ่มต้นของ Lisp เป็นรายการเชื่อมโยงทางเดียว (singly linked list) ซึ่งเหมาะสำหรับใช้เป็นโครงสร้างข้อมูลที่ไม่ถูกเปลี่ยนค่า

  2. มาโครของ Lisp เขียนง่ายเมื่อเทียบกับคู่แข่ง มาโครของ Rust นั้นทรงพลัง มาโครของ Rust นั้นเกี่ยวกับการแปลง TokenStream และมาโครของ Lisp นั้นเกี่ยวกับการเปลี่ยนรายการ (list) อย่างไรก็ตามฉันชอบที่จะแปลงรายการกว่าเปลี่ยน TokenStream เนื่องจากฉันไม่ต้องเรียนรู้โครงสร้างใหม่และคำสั่งใหม่สำหรับจัดการโครงสร้างใหม่

โดย Vee Satayamas ณ 22 March 2022 10:40 +0000

Two reasons that I think Lisp is interesting (2022 version)

  1. Lisp taught me to create functions that transform data step by step instead of adding mutable variables into loops. An immutable data structure is important for transforming data step by step without messing up existing data. Lisp's default data structure is a singly linked list.

  2. Lisp macro is easy to write compared to competitors. Rust's macro is powerful. Rust's macro is about transforming a TokenStream, and #Lisp's macro is about transforming a list. However, I prefer transforming a list to a TokenStream since I don't have to learn new structures and new commands for manipulating a new structure.

โดย Vee Satayamas ณ 22 March 2022 10:34 +0000

My Lispy code in Python

I translated my function in Lisp to Python.

def get_the_longest_prefix(a, b, ans):
    if len(a) == 0 or len(b) == 0:
        return "".join(ans)
    if a[0] == b[0]:
        return get_the_longest_prefix(a[1:], b[1:], ans + [a[0]])
    if a[0] == '-':
        return get_the_longest_prefix(a[1:], b, ans + ['-'])
    if b[0] == '-':
        return get_the_longest_prefix(a, b[1:], ans + ['-'])
    return "".join(ans)

And it has too many "return" statements. Anyway, I suppose Python doesn’t prefer a recursion. So this isn’t the case when we code in the Python way.

The code above concatenate Python's lists instead of creating a linked list like in Lisp.

So I made this experiment.

import time

n = 100

# Concatinating vectors
v = []
t1 = time.time_ns()
for i in range(n):
    v = [i] + v
t2 = time.time_ns()

# Creating cons cells for a singly link list
l = None
t3 = time.time_ns()
for i in range(n):
    l = (i, l) # Creating a cons cell
t4 = time.time_ns()

print((t2-t1)/(t4-t3))

At n = 100, concatenating vectors require 2 times of running time compared to creating cons cells for a linked list. So I keep using linked lists.

โดย Vee Satayamas ณ 22 March 2022 10:30 +0000

14 March 2022

Vee

How I install RocksDB

I want to install a specific version of RocksDB across different machines with different operating systems. Thus I can't rely on APT or other package management systems. I followed INSTALL.md by running make on the bundled Makefile, but last Saturday, I found that RocksDB couldn't read Snappy compressed data, although I installed the "libsnappy-dev" package. I tried many different ways to enable Snappy support. Then I decided to use CMake, which appeared only once in INSTALL.md. Now it works. My install script looks like this:

#!/bin/bash 
wget https://github.com/facebook/rocksdb/archive/refs/tags/v6.15.5.tar.gz -O - \ 
   | gzip -d \ 
   | tar -xf - \ 
   && pushd rocksdb-6.15.5 \ 
   && cmake -DWITH_SNAPPY=YES -DCMAKE_INSTALL_PREFIX=$MTDIR . \ 
   && DEBUG_LEVEL=0 PREFIX=$MTDIR make -j $(nproc) \ 
   && DEBUG_LEVEL=0 PREFIX=$MTDIR make install \ 
   && popd

$MTDIR is a target directory for installing RocksDB. DEBUG_LEVEL=0 and PREFIX=$MTDIR are perhaps not necessary.

โดย Vee Satayamas ณ 14 March 2022 08:06 +0000

27 February 2022

MrChoke

macOS Monterey DNS

เข้าใจเสมอว่าแก้ DNS ของ macOS สามารถแก้ใน /etc/resolv.conf ได้เลย แต่ล่าสุดพบว่ามันไม่ได้ให้ความสำคัญแล้ว ถ้าให้ได้ผลต้องตั้งใน UI ให้เรียบร้อย แต่ถ้ายังอยากใช้ command line เราก็ยังใช้ได้ดังนี้

ตรวจสอบอุปกรณ์

ก่อนที่เราจะตั้งต่าต้องรู้ก่อนว่าปัจจุบันเราต่อ network ผ่านอุปกรณ์ไหน และ มีอุปกรณ์อะไรบ้างในระบบ

networksetup -listallnetworkservices

ผลที่ได้จะประมาณนี้

Ethernet
USB 10/100/1000 LAN
Thunderbolt Bridge
Wi-Fi

เรียกดูค่าปัจจุบัน

ถ้าต่อ WiFi ก็แสดงดังนี้

networksetup -getdnsservers Wi-Fi

หรือ ถ้าใช้สายแลน

networksetup -getdnsservers Ethernet

ผลที่ได้จะประมาณนี้

networksetup -getdnsservers Ethernet
10.222.21.14
10.222.21.15
10.18.12.222
10.18.12.223

การตั้งค่า

หากต้องการล้มกระดานแล้วใช้ DNS ที่ต้องการตั้งครั้งนี้ก็ใช้คำสั่ง

networksetup -setdnsservers Ethernet 10.26.21.14 10.26.21.15

แต่ถ้าต้องการเพิ่มเข้าไปก็ให้ get ค่าเดิม แล้วต่อท้ายเข้าไป เช่น

networksetup -getdnsservers Ethernet
10.222.21.14
10.222.21.15

networksetup -setdnsservers Ethernet 10.222.21.14 10.222.21.15 10.18.12.222 10.18.12.223

หรือถ้าต้องการ clear ไปเลยก็

networksetup -setdnsservers Ethernet Emty

ลองเอาไปประยุกต์ใช้กันดูครับ

โดย MrChoke ณ 27 February 2022 05:28 +0000

25 February 2022

MrChoke

การใช้ Pydantic Model กับ Form Data ใน FastAPI

ปกติถ้าหากจำนวน Fields ใน Form มีไม่มากก็ไม่มีปัญหาเราสามารถเขียนแบบปกติได้ เช่น


@app.post("/abc", response_model=bool)
def abc(id: int = Form(...), name: str = Form(default=None),status: str = Form(default="single")):
  return True

แต่ถ้า Fields มีเยอะ ๆ สัก 10 Fields ขึ้นไป Code ก็จะเริ่มดูยากยาวมาก การใช้ Pydantic model เข้ามาช่วยก็จะจัดการง่ายขึ้น ปกติเราใช้ Pydantic model ใน Body แบบ JSON กันอยู่แล้วแต่สำหรับ Form Data จะมีลูกเล่นนิดหนึ่ง ค้นหาใน internet ไปเจอวิธีที่คิดว่าง่ายที่สุดละเลยบันทึกไว้สักหน่อย

ต้นฉบับที่ผมนำมาใช้

stackoverflow

ตัวอย่าง model.py

from typing import Optional

from fastapi import Form
from pydantic import BaseModel


def form_body(cls):
    cls.__signature__ = cls.__signature__.replace(
        parameters=[
            arg.replace(default=Form(default=arg.default) if arg.default is not inspect._empty else Form(...))
            for arg in cls.__signature__.parameters.values()
        ]
    )
    return cls

@form_body
class Profile(BaseModel):
    passport_no: Optional[str]
    hn: Optional[str]
    patient_guid: Optional[str]
    prefix: str = "นาย"
    first_name: str
    last_name: str
    prefix_eng: str = "Mr"
    first_name_eng: Optional[str]
    middle_name_eng: Optional[str]
    last_name_eng: Optional[str]
    gender: int = 1
    birth_date: Optional[str]
    mobile_phone: Optional[str]
    installed_line_connect: Optional[str]
    address: Optional[str]
    moo: Optional[str]
    road: Optional[str]
    chw_code: Optional[str]
    amp_code: Optional[str]
    tmb_code: Optional[str]
    address_full_thai: Optional[str]
    address_full_english: Optional[str]
    nationality: Optional[str]

ในส่วนของ Form มันจะมีหลัก ๆ อยู่สามแบบคือ

ผมเลยปรับในตัว Decorators เพิ่มเติมให้มันปรับตาม Model ที่เรากำหนดไว้

arg.replace(default=Form(default=arg.default) if arg.default is not inspect._empty else Form(...))

ตัวอย่าง Router

from model import Profile
from fastapi import Depends, FastAPI, File, UploadFile

app = FastAPI()

@app.post("/abc", response_model=bool)
def abc(profile: Profile = Depends(Profile), photo: UploadFile = File(...)):

  return True

ตอน Render ก็จะได้ประมาณนี้

Swagger

Swagger input with default

ลองเอาไปประยุกต์ใช้กันดูครับ

โดย MrChoke ณ 25 February 2022 03:44 +0000

20 February 2022

MrChoke

การเลือก WebCam บน Google Colab ในกรณีมีมากกว่า 1 ตัว

ปกติ Colab ได้เตรียม Snippet เกี่ยวกับการใช้ WebCam มาให้เราแล้วแค่เลือก Code มาใช้แบบง่าย ๆ เลย

พอดีผมทดสอบ API แล้วใช้ Colab ในการทดสอบ และ แสดงตัวอย่างให้กับ Dev คนอื่น ๆ เลยแก้ไข script นิดหน่อยให้สามารถเลือกกล้องได้ถ้าเราเสียบกล้องมากกว่า 1 ตัว

Code ตัวอย่าง

Google Colaboratory

GitHub logo mrchoke / select_webcam_in_colab

Select WebCam in Google Colab

select_webcam_in_colab

Select WebCam in Google Colab




JavaScript

จริง ๆ ผมใช้ JavaScript ที่ใช้งานเป็นประจำอยู่แล้ว ซึ่งสามารถนำไปประยุกต์ใช้กับ JavaScript Framework อื่น ๆ ได้ด้วย

ตัวอย่าง

// define variables
let camera_id = ''; // current camera id
let stream = null; // current stream
let devices = []; // all camera
const div = document.createElement('div'); // parent div playground
const camera_list = document.createElement('select'); // camera dropdown
const camera_label = document.createElement('h3'); // current selected camera
const video = document.createElement('video'); // video object

// scan all cameras
async function scan_cameras() {
  let camera_found = false;

  await navigator.mediaDevices
      .enumerateDevices()
      .then((deviceInfos) => {
        deviceInfos.forEach((deviceInfo) => {
           if (deviceInfo.kind === 'videoinput') {
            if(!camera_found) {
              camera_label.textContent = `Camera: ${deviceInfo.label}`;
              camera_id = deviceInfo.deviceId
              camera_found = true
            }
            devices.push(deviceInfo)
          }
        })

      })

  div.appendChild(camera_label);
 }

// change camera by id
async function change_camera(e) {
  camera_id = e.target.value;
  camera_label.textContent = `Camera: ${devices.filter(i => i.deviceId == camera_id)[0].label}`;
  await start_camera();
}

// stop all active camera
async function stop_camera() {
  if(stream){
    await stream.getVideoTracks().forEach(track => track.stop());
  }
}

// start camera by selected id
async function start_camera() {
  await stop_camera();
  stream = await navigator.mediaDevices.getUserMedia({video: {deviceId: {exact: camera_id}, audio: false}});
 video.srcObject = stream;
 await video.play();
  }

// create camera list dropdown
function create_camera_select() {
 camera_list.appendChild(new Option("Select Camera", 0))        

 devices.forEach((device, i) => {
 camera_list.appendChild(new Option(device.label || `Camera ${i + 1}`, device.deviceId));
   })

 div.appendChild(camera_list);
 camera_list.addEventListener("change", change_camera);

}

// take photo
async function takePhoto(quality) {
  await scan_cameras();
  create_camera_select();

  const capture = document.createElement('button');
  capture.textContent = 'Capture';
  div.appendChild(capture);

  video.style.display = 'block';
  document.body.appendChild(div);
  div.appendChild(video);
  await start_camera();

   google.colab.output.setIframeHeight(document.documentElement.scrollHeight, true);

  // Wait for Capture to be clicked.
  await new Promise((resolve) => capture.onclick = resolve);

  const canvas = document.createElement('canvas');
  canvas.width = video.videoWidth;
  canvas.height = video.videoHeight;
  canvas.getContext('2d').drawImage(video, 0, 0);
  await stop_camera();
  div.remove();
  return canvas.toDataURL('image/jpeg', quality);
  }

ตัวอย่างการเลือกกล้อง WebCam
ตัวอย่างการเลือกกล้อง WebCam

ลองเอาไปประยุกต์ใช้กันดูนะครับ น่าจะได้กับการเลือกไมค์บันทึกเสียงได้ด้วยแต่ผมไม่เคยลอง คือหลักการเดียวกันเลย

โดย MrChoke ณ 20 February 2022 20:22 +0000

14 February 2022

Vee

บทเรียนจากแบบฝึกหัดโปรแกรมตัดเกรด

โปรแกรมตัดเกรดเป็นตัวอย่างของ business logic ที่ดีเลยครับ เพราะว่าหลายครั้งก็มีการเปลี่ยนแปลงกฎเกณฑ์ วิธีคิดเกรดของแต่ละสถาบันไม่เหมือนกันก็ได้ บางวิชาในสถาบันเดียวกันก็ตัดเกรดไม่เหมือนกัน บางมีชามี A B C D F บางวิชามีเกรดแค่ S กับ U

อย่างไรก็ตามวิธีคิดเกรดในประเทศไทยมันไม่ค่อยเปลี่ยนแปลงอาจจะไม่เห็นภาพ ถ้าจะปรับให้เหมาะกับบริบทประเทศไทยควรจะเปลี่ยนโจทย์เป็นการคำนวณว่าใครได้เป็นส.ส.และส.ว.บ้างเพราะว่ารัฐธรรมนูญและกฎหมายลูกเปลี่ยนบ่อยเหลือเกิน และยังมีประเด็นในการตีความด้วย

เพราะว่าโปรแกรมเปลี่ยนตามกฎเกณฑ์ต่าง ๆ ซึ่งบางทีก็เปลี่ยนสามเดือนหลังจากที่เขียน บางทีก็สามปี บางทีก็สิบปี คนที่มาแก้ไขก็อาจจะเป็นคนละคนกับที่เขียนทีแรก

ถ้าอยากให้โปรแกรมแก้ไม่ยาก ก็จำเป็นต้องเดาใจคนอื่น เดาใจตัวเอง ว่าเขียนแบบไหนแล้วจะกลับมาอ่านง่ายหลังจากผ่านไปหลายปี และเขียนแบบไหนถึงจะกลับมาแก้ง่ายหลังจากผ่านไปหลายปี เป็นต้น

เรื่องว่าโปรแกรมทำงานช้าเร็วใช้หน่วยความจำมากน้อยก็สำคัญ แต่ก็ต้องพิจารณาเป็นกรณีไป หลายกรณีที่โปรแกรมมีประสิทธิภาพพออยู่ก็ไม่จำเป็นต้องไปปรับแต่งอีก

โดย Vee Satayamas ณ 14 February 2022 07:56 +0000

28 January 2022

Vee

My opinion about Node.js

I agree that using JavaScript increases the chance of participation. I released a few versions of Thai word breakers in different programming languages. One on node.js is the most popular. 8 people contributed to the JS-based project compared to 2-3 people in other programming languages. However, JS has a downside too. In 2017, @iporsut and I made an experiment to compare Thai word breakers that we created. JS version running time is 15X of the Rust version. Even by comparing with another dynamic language, the Julia version is faster than the one in JS.

I created a website using node.js in 2014, and it is still running. The performance is good. However, I have a few regrets.

โดย Vee Satayamas ณ 28 January 2022 14:29 +0000

11 December 2021

Kitt

inception ?

I knew I’m in a dream. It was a bad dream. I wanted to get out I knew I’m in a dream. I decided to commit a suicide so it would wake me up. I jumped out of a window, closed my eyes. It was pitch black. Eyes opened, I woke up .. .. in … Continue reading inception ?

โดย kitty ณ 11 December 2021 16:51 +0000

1 December 2021

Vee

โม้ว่าทำอะไรเกี่ยวกับ Rust บ้าง 2021

ทีแรกผมจะเขียนเตรียมไว้พูดในงาน Rustacean Bangkok 2.0.0 แต่ว่าวันงานไม่ว่าง ก็เลยเอามารวมเป็น blog ไว้ก่อนแล้วกัน

ผมจะเล่าว่าผมทำอะไรกับภาษา Rust บ้างซึ่งก็คงไม่ครบถ้วน จะเขียนไปตามที่นึกได้

2014

ผมเริ่มเขียน Rust ครั้งแรกค.ศ. 2014 อย่างน้อยก็ที่ใส่ gist ไว้ code

#[deriving(Encodable, Decodable, Show, Clone)]
pub struct Node {
    pub snode: ~[~[Range]],
    pub children: Option<~[Option<Node>]>,
}

เป็นช่วงก่อนที่จะออก Rust 1.0 หน้าตาก็ต่างจากเดี๋ยวนี้เยอะเหมือนกัน ส่วนมากเอามาทำงานที่เอาไว้เก็บ string-tree alignment แต่รายละเอียดเรื่องนี้ก็ข้ามไปดีกว่า

2015

ค.ศ. 2015 ผมนึกถึงโปรแกรมพื้นฐานที่ใช้บ่อย ๆ โปรแกรมตัดคำ ถ้าอยากให้ความถูกต้องสูงใช้ deepcut เลย ถ้าอยากให้ความถูกต้องสูงแต่เร็วขึ้นหน่อยใช้ attacut

แต่ถ้าเอาเร็ว libthai chamkho icu

chamkho ผมแบ่งขั้นตอนการตัดคำเป็น 3 ขั้น

  1. สร้าง directed acyclic graph (DAG) ของวิธีตัดคำที่เป็นไปได้ทั้งหมด
  2. หา shortest path บน DAG จากข้อ 1
  3. ถอด path จากข้อสองมาเป็นตำแหน่งของ string ที่ต้องตัด

ส่วนที่ปรับไปมาคือข้อ 1 ผมแยกแบบนี้

1.1 สร้าง edge จากคำในพจนานุกรมที่ตรงกับ sub-string
1.2 สร้าง edge
1.3 ตัด edge ที่ขัดกับกด cluster ซึ่งเป็น substring ที่ตัดไม่ได้ออก ข้อนี้เพิ่มมาในค.ศ. 2021

มีแค่นี้เลย เล่าย้อนกลับไปหน่อยว่าปลายค.ศ. 2002 ผมเริ่มทำโปรแกรมตัดคำออกมาเขียนด้วย C

เพราะผมลองใช้ NLTK ที่เขียนด้วย Python ทั้งตัวแล้วมันทำงานช้าเกิน

แต่ผมก็คิดว่าแอปก็ยังควรจะภาษาที่เขียนง่าย ๆ แบบ Python หรือ Ruby หรือ Lisp อยู่ดี

แต่ข้อจำกัดของตัวคำสมัยนั้นคือ

  1. หลายตัวเขียนด้วย C++ ผมเขียนไม่เป็นหลังจากเรียนอยู่นาน

  2. ไม่ค่อยเหมาะเอามา bind กับ Ruby และอื่น ๆ

  3. แก้ word list และกฎยาก

thaiwordseg ก็เลยใส่ word list ใน file กฎใช้ regex และ bind กับ Ruby มาให้เลย ดูโครงการได้ที่ sourceforge ยังมี package ของโครงการนี้อยู่บน SUSE Linux ด้วยครับ

ปลายค.ศ. 2015 ผมไปพูดเรื่อง Rust ใน Barcamp Bangkhen รอบนึง พูดประมาณในเอกสารนี้

2016

ค.ศ. 2016 พูดเรื่อง Rust ในกลุ่ม Mozilla ไทย มีหลงเหลือ slide อยู่บ้าง

ค.ศ. 2016 ตั้งกลุ่ม Facebook สำหรับชาว Rust ไทยขึ้นมา ถ้าจำไม่ผิดตั้งตามกลุ่ม Clojure ไทยเพราะเห็นว่ามีกลุ่มขึ้นมาก็ดี ทุกวันนี้กลุ่ม RustLangTH ก็ได้คุณ Nui Narongwet และคุณ Wasawat Somno ช่วยกันดูแลเป็นหลักเลย กราบขอบพระคุณครับ

2017

ค.ศ. 2017 ร่วมกับคุณ @iporsut ทดสอบ Rust ที่เอามาตัดคำโดยใช้ word list และยังไม่ได้ใช้ regex สรุปได้เลยว่าเอา Rust เร็วกว่า Go Java คือตัวที่เขียนด้วย Rust ใช้เวลาทำงานเพียง 60% ของ Go ยังไม่ต้องพูดถึงตัวอื่นที่ว่าช้ากว่านั้น

ส่วนพวก Python JavaScript Clojure คือช้ามาก ห่างกันเป็นเท่าตัว ดูรายละเอียดเพิ่มเติม

ค.ศ. 2017 พบปะชาว Rust ไทยไปอีกรอบนึง ตาม blog นี้

ค.ศ. 2017 ตั้งกลุ่ม Rust ไทยบน Telegram ทั้งกลุ่มใน Telegram ทั้ง Facebook ผมส่งมอบสิทธิ admin ไปหมดแล้ว สมมุติว่าตอนนี้ผมตายไปก็ไม่ผลกระทบแน่นอน

ค.ศ.2017 ผมแก้ bug บน servo พอมีบันทึกไว้ใน blog ของ servo ก็พบว่าเป็น bug ที่ผิดง่าย ๆ ใส่ตัวแปรสลับกัน

Rust จะประกาศ type ให้ต่างกันจน compiler ตรวจได้ว่าคนใส่สลับกันได้

แต่ก็แบบที่ให้เวลาใช้งานจริง ๆ ก็ไม่ได้ประกาศ type แบบที่ว่า i32 u32 เต็มไปหมด ใส่สลับกัน compiler ก็ตรวจไม่ได้อยู่ดี

ระหว่างผมเขียน Python กับ Rust รู้สึกว่าเขียน Rust ก็เกิด bug เยอะแยะอยู่ดี อาจจะว่าผมโง่ก็ได้ แต่มันเป็นตัวแปรควบคุมว่าผมก็โง่เหมือนเดิมเวลาเขียน Python หรือ Rust ก็ตาม 😛

2018

ค.ศ. 2018 ส่วนมากก็จะปรับปรุงโปรแกรมเก่า เริ่มเขียน HTTP server มาหุ้มตัวตัดคำบ้าง โดยใช้ Hyper ตอนนี้น่าจะรันไม่ได้แล้ว หน้าตาแบบข้างล่าง

impl Service for WordcutServer {
    type Request = Request;
    type Response = Response;
    type Error = hyper::Error;
    type Future = WebFuture;


    fn call(&self, req: Request) -> Self::Future {
        match (req.method(), req.path()) {
            (&Post, "/wordseg") => wordseg_handler(req),
            (&Post, "/dag") => dag_handler(req),
            _ => not_found(req)
        }
    }
}

2019

ค.ศ.2019 ส่วนมากจะเป็น code พวก string-tree เหมือนห้าปีก่อน แต่ style ก็เปลี่ยนไปเยอะ หน้าตาประมาณข้างล่าง

quick_error! {
    #[derive(Debug)]
    pub enum TextunitLoadingError {
        CannotLoadToks(lang: LangKey, err: Box<Error>) { }
        CannotLoadLines(lang: LangKey, err: Box<Error>) { }
        CannotLoadLinks(err: Box<Error>) { }
        CannotAlignToks(lang: LangKey, line_no: usize, err: Box<Error>) { }
    }
}

#[derive(Debug)]
pub struct Textunit {
    pub bi_text: BiText,
    pub bi_rtoks: BiRToks,
    pub links: Vec<Link>,
}

2020

ค.ศ. 2020 ผม port extension สำหรับ full-text search ภาษาไทยบน PostgreSQL ของคุณ zdk (Di Warachet S.) มาเป็น Rust

ผมไม่ทราบว่าคุณ zdk คิดอย่างไร แต่ผมมองว่าการลงพวก ElasticSearch เพื่อที่จะใช้ full text search พื้น ๆ หรือแม้แต่เอามา query JSON document บนเว็บที่คนใช้พร้อมกันไม่ถึง 100% คน มันเพิ่มงาน กิน RAM กิน SSD ขึ้นอีกเยอะ ใช้ PostgreSQL แทนได้น่าจะดีกว่า ตัวที่เขียนด้วย Rust ผมตั้งชื่อให้ว่าชำฆ้อพีจี

ค.ศ. 2020 ผมเขียน JSON parser สำหรับ GNU Guile ชื่อ guile-json-parse ด้วย

2021

ค.ศ. 2021 ผมแก้ให้โปรแกรมตัดคำมี regular expression แบบ newmm และ nlpo3 แต่พบว่าทุกอย่างที่เคย optimize มาสูญสลายไปเกือบหมดเลย เพราะว่า regex แบบ fancy ใน Rust มันก็ไม่ได้เร็วกว่า regex engine ของ Python ที่เขียนด้วย C 😰

ถ้าจะคงความเร็วไว้อาจจะต้องเขียนกฎให้เป็นภาษา Rust เลยแบบที่ chamkho เคยทำมา หรือทำตาม libthai ที่มีกฎคล้าย ๆ newmm แต่เขียนด้วย C วิธีนี้เร็วแน่ แต่ว่าแก้ทีเหนื่อย จะเพิ่มภาษาใหม่หนักแรง

แต่ Rust มีทางออกที่สามคือใช้ regex-automata ของ Andrew Gallant ซึ่งตัวนี้ตามชื่อแปลง regex เป็น automata แบบที่ควรจะเป็น แต่ก็จะรับ regex แบบขยายของ newmm ไม่ได้ก็เลยต้องโม regex บ้าง

ทำให้เสร็จแล้ว newmm รันบน xeon ใช้เวลารัน 9.65 เท่าของ chamkho ผมว่าควรจะเขียนถึงเรื่อง profiling ด้วยแค่จะแยกไปอีกโพสต์

อีกภาษาที่หลัง ๆ ทำงานเร็วขึ้นมาก และสำหรับผมเขียนง่ายกว่า Rust เยอะมาก ๆ คือ Julia แต่ว่าก็ติดอยู่ 3 เรื่องคือ

  1. ไม่มี regex-automata บน Julia
  2. runtime ของ Julia เปิดช้า
  3. ทำ lib ลอกเลียนภาษา C ลำบาก ทำให้เอาไปใช้กับ cffi ใน Common Lisp หรือ Ruby ยาก

Common Lisp ก็เป็นอีกภาษาหนึ่งที่ optimize มาก ๆ แล้วก็เร็วสูสี Julia แต่ก็ติดประเด็นเดียวกันทั้งหมด

ทำให้ผมสรุปแบบนี้เลยว่า Rust นอกจากภาษาและ compiler แล้วยังเป็น community ที่ lib ดี ๆ ออกมาเยอะมาก หลายตัวหาในภาษาอื่นยาก

ค.ศ. 2021 อีกอย่างที่คือ เพื่อที่จะไป bind กับภาษาอื่นง่าย ๆ chamkho มี wrapper ทำให้กลายเป็น library ภาษา C แบบปลอม ๆ ชื่อ wordcutw

พอทำแบบนี้เวลาจะ bind กับ Ruby หรือภาษาอื่น ๆ ก็ไม่ต้องคิดอะไรใหม่เลยใช้ FFI ได้เลย ข้อดีของการใช้ FFI หรือ cffi คือมันจะไม่ยึดติดกับ implementation ตัวใดตัวหนึ่ง เช่น Common Lisp อาจจะใช้ implementation เป็น SBCL หรือ Clozure CL ก็ได้ แต่ถ้าใช้ cffi มา bind กับ wordcutw ก็จะใช้ได้ทั้ง SBCL และ Clozure CL โดยไม่ต้องแก้ code เลย

ค.ศ. 2021 ผมคิดว่าน่าจะไป chat กันแบบ protocol ที่เปิดและ decentralize บ้างก็เลยเปิด กลุ่ม Rust ไทยบน Matrix

ค.ศ. 2021 อีกอย่างที่ทำคือเทียบความเร็ว Apache Arrow กับ Pandas ผลคือถ้าเขียน Pandas ดี ๆ ก็เร็วได้ แต่ใช้ Arrow เร็วกว่า ยิ่งใช้ Rust ก็เร็วใหญ่ และไม่ต้องระวังเรื่องแปลงไปมาระหว่าง type ของ Rust และ Python ดูรายละเอียดเพิ่มเติม

ค.ศ. 2021 ก็มีเรื่องอื่น ๆ คือเขียน parser สำหรับ Apertium stream format โดยใช้ Nom ทำให้เขียน parser ง่ายขึ้นมาก; port โปรแกรม Attacut มาบน Rust ให้ชื่อว่า Khatson ปรากฎว่าไม่เร็วขึ้นเลยแต่ทำให้รู้ว่าใช้ tch ใน Rust ที่หุ้ม PyTorch ไว้ให้ เขียนพวก deep learning แบบเดี๋ยวกับใช้ PyTorch สะดวกมาก และสำหรับผมงงน้อยกว่าใช้ PyTorch ตรง ๆ

สถานการณ์ของ Rust ในไทย

สภานการณ์ปัจจุบัน ตอนนี้ผมคิดว่า Rust ติดลมบนไปแล้ว บริษัทขวัญใจมหาชนก็เป็นสมาชิก Rust Foundation ทั้ง AWS Facebook Google Huawei Microsoft Mozilla; celeb ก็เขียน Rust กันแล้ว สถานการณ์ในไทยก็ฝืนกระแสโลกไม่ไหวแน่ ๆ

ส่วนที่เป็นรูปธรรมมากคือคุณ Jojo จากช่อง KubeOps Skills ทำวิดีโอภาษาไทยขึ้นมาสอนเขียน Rust เลย ผมว่าอันนี้ดีมาก ๆ และตามที่เกริ่นไปคุณ Natechewin Suthison จะจัดงาน Rustacean Bangkok 2.0.0 วันที่ 13 ธันวาคม พ.ศ.2564 นี ทุกท่านกดดูรายละเอียดตาม link ไป Facebook ได้เลย

โดย Vee Satayamas ณ 1 December 2021 09:27 +0000

15 November 2021

Vee

Install Emacs 27.2 on openSUSE LEAP 15.3 from a source tarball

Install pre-requisited tools

sudo zypper install make gcc automake autoconf libtool aclocal wget
sudo zypper si -d emacs

Download Emacs source tarball

wget https://ftp.gnu.org/pub/gnu/emacs/emacs-27.2.tar.gz

Extract and change directory

tar xzvf emacs-27.2.tar.gz
cd emacs-27.2

Generate makefiles

./configure --prefix=/opt > log

Make

make -j `nproc`

Install

sudo make install

Add path

Add the line below to ~/.bashrc by nano ~/.bashrc

export PATH="/opt/bin:$PATH"

โดย Vee Satayamas ณ 15 November 2021 05:15 +0000

12 November 2021

Vee

An incomplete WebSocket client based on only socket, ssl, and uuid in Python

A few days ago, I couldn't get a WebSocket library working with another library on Python 3.10. So to avoid those dependencies, I implemented my WebSocket client on a low-level socket API. I implemented one in Common Lisp first. Then I translated it to Python. My WebSocket client is very far away from being completed, but at least it can run.

The first part is import. I imported only three things that are socket, ssl, and uuid.

import socket
import ssl
import uuid

The second part is based on HTTP headers for telling the server to switch to the WebSocket mode.

def upgrade(s, conn_info):
    with s.makefile(mode = 'rw', encoding = "ISO-8859-1") as f:
        f.write(f'GET {conn_info["path"]} HTTP/1.1\r\n')
        f.write(f'Host: {conn_info["host"]}\r\n')
        f.write("Connection: Upgrade\r\n")
        f.write("Upgrade: websocket\r\n")
        f.write(f'Sec-Websocket-Key: {str(uuid.uuid1())}\r\n')
        f.write("Sec-WebSocket-Version: 13\r\n")
        f.write("\r\n")
        f.flush()

        # reading response
        for line in f:
            if line == "\n":
                break

The third part is reading a content length. I assume that the server keep sending text contents. In read_payload_len(), s.recv(1) read a byte from the socket s. & 0x7F is for masking only 7 bits. If length is 127, the length is in the next 4 bytes instead. If length is 126, the length is in the next 2 bytes. Otherwise the function just returns the length.

def read_payload_len(s):
    match s.recv(1)[0] & 0x7F:
        case 127:
            return read_extra_len(s, 4)
        case 126:
            return read_extra_len(s, 2)
        case l:
            return l

The read_extra_len reads bytes and turn them to integer.

def read_extra_len(s, num_of_bytes):
    buf = s.recv(num_of_bytes)
    len = 0
    for i in range(num_of_bytes):
        len += buf[i] << (8 * (num_of_bytes - i - 1))
    return len

In the fourth part, we read a web socket frame. The program determine a frame type from opcode, which in the last 4 bits of the header. I should implement PING-PONG part but I didn't. According to RFC6455, which I forgot to mention before, opcode == 0x1 means the frame is a text frame. So the program reads payload length and reads the payload.

def read_frame(s):
    header0 = s.recv(1)[0]
    opcode = header0 & 0x0F
    match opcode:
        case 0x1:
            payload_len = read_payload_len(s)
            print(s.recv(payload_len))
        case 0x9:
            print("PING")
        case 0xA:
            print("PONG")

The last part is for opening connections and SSL/TLS wrapper. The function created a socket and wrapped it with TLS/SSL wrapper. The sending upgrade message to ask the server to switch to Websocket mode and the keep reading frames.

def connect(conn_info):
    ctx = ssl.create_default_context()
    with socket.create_connection((conn_info["host"], conn_info["port"])) as s:
        with ctx.wrap_socket(s, server_hostname = conn_info["host"]) as ss:
            upgrade(ss, conn_info)
            while True:
                read_frame(ss)

In the final part, I cannot find any public Websocket endpoint besides ones from cryptocurrency exchanges. So I put Bitkub API.

connect({"host": "api.bitkub.com",
         "port": 443,
         "path": "/websocket-api/market.trade.thb_btc"})

And it works, but if you are unlucky, you will get PING instead.

> python3.10 http_ex.py 
b'{"amt":0.00004661,"bid":86911896,"rat":2140000,"sid":82187323,"stream":"market.trade.thb_btc","sym":"THB_BTC","ts":1636729677,"txn":"BTCSELL0011078574"}\n{"amt":0.23306074,"bid":86912246,"rat":2140000,"sid":82187325,"stream":"market.trade.thb_btc","sym":"THB_BTC","ts":1636729677,"txn":"BTCSELL0011078576"}'
b'{"amt":0.00466121,"bid":86912014,"rat":2140000,"sid":82187324,"stream":"market.trade.thb_btc","sym":"THB_BTC","ts":1636729677,"txn":"BTCSELL0011078575"}'

โดย Vee Satayamas ณ 12 November 2021 15:08 +0000

10 November 2021

Vee

Installing Python 3.10 on openSUSE Leap 15.3

Prerequisites

sudo zypper install gcc-c++ make gcc automake autoconf libtool aclocal
sudo zypper si -d python3

Download and unarchive

Download Python-3.10.0.tar.xz; tar xJvf Python-3.10.0.tar.xz; cd Python-3.10.0

Configure

I got this error message by running ./configure

./configure: line 10530: PKG_PROG_PKG_CONFIG: command not found

So I ran this:

docker run --rm --pull=always -v $(pwd):/src quay.io/tiran/cpython_autoconf:latest

Still configure cannot find g++, so I added CXX=c++ in front of configure.

So this was how I used configure:

CXX=c++ ./configure --enable-optimizations --prefix=/opt 2> elog > log

make

This step has no problem.

make -j8
sudo make install

Running python

pip3.10 and python3.10 cannot run since it can't find readline.

So I ran this.

ln -s /opt/lib64/python3.10/lib-dynload /opt/lib/python3.10/lib-dynload

Then python3.10 works now.

P.S. Thank every one who provide answers on issue trackers and forums. I should have kept URLs to those. Sorry that I didn't.

โดย Vee Satayamas ณ 10 November 2021 07:53 +0000

23 October 2021

Thep

IBus-LibThai 0.1.5

IBus-LibThai 0.1.5 ออกไปแล้วเมื่อวันพุธที่ผ่านมา หลังจากไม่มี release มาเกือบ 5 ปี โดยมีเงื่อนไขหลักที่ทำให้จำเป็นต้องออกรุ่นใหม่คือปัญหาในระบบตั้งค่า อาการคือตั้งค่าเสร็จแล้วไม่มีผลต่อตัว engine ตั้งยังไงก็ได้ค่า default

ผมไม่ได้ติดตามการพัฒนาของ IBus มาระยะหนึ่ง จึงไม่ทราบว่ามีความเปลี่ยนแปลงอะไรบ้างจนทำให้วิธีตั้งค่าแบบเดิมใช้ไม่ได้ จึงเริ่มจากตรวจสอบ engine อื่นว่าปัจจุบันเขาทำยังไงกัน ก็ได้พบว่าหลาย engine ได้ย้ายจากการใช้ IBusConfig ซึ่งเป็น API ของ IBus เองสำหรับการจัดการค่าตั้งไปใช้ระบบอื่น บาง engine ถึงกับสร้างระบบ config ของตัวเองขึ้นมาเลย

แต่ผมก็ยังคงพยายามปรับแก้ภายใต้ API ของ IBus ต่อไป โดยเปลี่ยนจากการ restart IBus เพื่อ reload config มาเป็นการ watch ค่า config แล้วปรับใช้ค่าที่เปลี่ยนทันทีแทน ซึ่งก็ทำให้ engine เปลี่ยนพฤติกรรมตามค่าที่ผู้ใช้ตั้งได้ ภายใน session เดิม แต่ไม่สามารถ save ค่าไว้ให้มีผลใน session ถัดไปได้

ผมแน่ใจว่าทำทุกอย่างเท่าที่ document บอกแล้ว ถ้าจะมีอะไรที่ undocumented ก็ต้องแกะซอร์สของ IBus ซึ่งพอลงมือแกะก็พบว่า แม้แต่ IBus แกนหลักเองก็ไม่ใช้ IBusConfig แต่ไปใช้ GSettings ของ GLib!

จะรออะไรล่ะครับ ถึงแกะจนเจอที่ผิด แต่เขาจะสนใจแก้โค้ดที่เขาเองก็ไม่ใช้ไหมล่ะ? ผมจึงตัดสินใจเปลี่ยนไปใช้ GSettings เหมือนกัน

ด้วยระบบ GSettings ซึ่งใช้ dconf เป็น backend ค่าตั้งต่างๆ จะสามารถเข้าถึงได้ด้วยโปรแกรม dconf-editor

ค่า config ของ IBus-LibThai ใน dconf editor

โดยค่า config แต่ละค่าจะมีคำอธิบายจาก GSchema ที่เตรียมไว้

คำบรรยายคีย์ kb-layout ของ IBus-LibThai ใน dconf editor

และผู้ใช้ IBus ก็ยังคงสามารถปรับแต่ง IBus-LibThai ได้จาก preferences ของ engine ใน IBus Preferences ตามปกติ

Preferences ของ IBus-LibThai

พร้อมกันนี้ ยังมีความเปลี่ยนแปลงอื่นในส่วนของผังแป้นพิมพ์:

โดย Thep (noreply@blogger.com) ณ 23 October 2021 15:58 +0000

24 September 2021

Vee

A few remarks about actor and its supervisor in Actix

Actix has a supervisor that helps to restart an actor under its supervision. However, sometimes it doesn't. 😹

AFAIK something has to send a Die (message) to an actor when something went wrong. The actor has to implement a Die message handler. In the handler, it calls ctx.stop (context stop). When "Actor::stopping(...) == Running::Stop", its poll function will return Poll::Ready. Then its supervisor can perform restarting.

In my case, all of these didn't work because of two reasons.

  1. Actor didn't stop properly. It stopped without calling the stopped function. So I suppose it has no chance to handle the Die message.

  2. Actor kept using the CPU when it went wrong. So the poll function was blocked. Then the actor cannot handle the Die message.

I fixed the first case by calling tokio::time::sleep, and the second case by breaking for the loop.

PS I'm not sure about what I wrote. Please tell me if I missed or misunderstood some things or even everything.

โดย Vee Satayamas ณ 24 September 2021 16:24 +0000

18 September 2021

Thep

Red-Black Trees

Red-black tree เป็น self-balancing binary search tree ที่ใช้ในโครงการต่างๆ มากมาย เช่น ใน C++ STL, ใน symbol table ของ compiler/interpreter ต่างๆ และใน Linux kernel ด้วยข้อได้เปรียบ AVL tree เรื่องการเพิ่มข้อมูลเพื่อการ balance ในแต่ละโหนดเพียงบิตเดียว ในขณะที่ AVL tree ต้องการอย่างน้อย 2 บิต รวมถึงการ insert และ delete ที่เร็วกว่า แม้จะใช้เวลา search มากกว่าสักหน่อย แต่ก็ยังเป็น O(log n) เหมือนกัน

ฟังดูน่าศึกษาใช่ไหมล่ะ? แต่พอลองค้นดูจริงๆ มันยุ่งกว่าที่คิดมาก

red-black tree เสนอโดย Leonidas J. Guibas และ Robert Sedgewick (PDF) โดยต่อยอดมาจาก symmetric binary B-tree ของ Rudolf Bayer

symmetric binary B-Tree เป็นการสร้าง perfectly-balanced 2-3-4 tree (B-tree ที่มีทางแยกไม่เกิน 4 และทุก leaf มีความลึกเท่ากันหมด) ด้วย binary tree แบบพิเศษ โดยแทนโหนด B-tree ที่มีทางแยก 3 และ 4 ทางด้วย binary node 2 และ 3 โหนดที่เชื่อมกันด้วยลิงก์แนวนอน (เรียกว่า ρ-arc) ส่วนลิงก์ที่เชื่อมระหว่างโหนดของ B-tree ก็เป็นลิงก์แนวตั้ง (เรียกว่า δ-arc)

Guibas และ Sedgewick ได้นำ symmetric binary B-tree มาสร้างคำอธิบายแบบใหม่ โดยให้ทุกลิงก์เป็นลิงก์แนวตั้งทั้งหมด และเรียก ρ-arc ของ Bayer ว่า red link และเรียก δ-arc ว่า black link แล้วเรียบเรียงนิยามและอัลกอริทึมต่างๆ เสียใหม่ กลายเป็น red-black tree

Red-black representation of a 2-3-4 tree
(ภาพจาก Left-leaning Red-Black Trees โดย Robert Sedgewick)

ส่วนเหตุผลว่าทำไมต้องเป็นสองสีนี้ Sedgewick ตอบไว้ในตอนท้ายของการบรรยายครั้งหนึ่งว่า เป็นเพราะขณะที่พัฒนาเรื่องนี้กันที่ Xerox PARC นั้น เริ่มมีการสร้าง laser printer ที่สามารถพิมพ์สีได้ และสีที่พิมพ์ออกมาดูดีที่สุดในขณะนั้นคือสีแดง

อัลกอริทึมสำหรับ insert ของ red-black tree ค่อนข้างจะเทียบเคียงกับของ 2-3-4 tree ได้อย่างตรงไปตรงมา และใช้ operation พื้นฐานในการ rebalance คล้ายกับของ AVL tree แต่อัลกอริทึมสำหรับ delete นั้น Guibas และ Sedgewick บรรยายไว้ไม่ยาวมาก และยังดูมีขั้นตอนเกินจำเป็น โดยมีการ rebalance ทั้งขาลงและขาขึ้น

หลายตำราจึงคิดโครงสร้างของตัวเองขึ้นมาเพื่อปรับปรุงการ delete ซึ่งหลายกรณีส่งผลถึงวิธีอธิบายโครงสร้างทั้งหมดที่ไม่เชื่อมโยงกับ 2-3-4 tree อีกต่อไป เช่น เรียก red/black node แทน red/black link และอธิบายโครงสร้างในเทอมของ parent/sibling/uncle node ไปเลย ซึ่งทำให้จำเป็นต้องมี parent pointer และไม่อาศัย recursion และพอมาถึงเรื่องการ delete หลายตำราก็อธิบายได้น่าปวดหัวมาก บางฉบับถึงกับจั่วหัวไว้ก่อนเลยว่า You will hate it! บางฉบับข้ามเรื่อง delete ไปเสียดื้อๆ เลยด้วยซ้ำ

Red-black tree with red/black nodes
Red-black tree ที่ใช้ red/black node
(ภาพจาก Wikipedia)

หลังจากอ่านมาหลายแหล่ง รวมถึงฉบับที่ Sedgewick กลับมาเองด้วยโครงสร้าง Left-leaning red-black tree (slide, Coursera) พร้อมกับโค้ดสุด simple แต่ก็มีปัญหาเรื่องการ delete ที่ช้ากว่าของตำราอื่น ดังบทวิจารณ์ Left-Leaning Red-Black Trees Considered Harmful

แต่สุดท้ายก็เจอคำอธิบายที่ผมคิดว่าลงตัวที่สุดจากคอร์สหนึ่งของมหาวิทยาลัย Purdue ซึ่งอธิบาย Red-black tree แบบไม่มี leaning โดยอิง 2-3-4 tree ตั้งแต่ต้นจนจบ อ่านได้ตามลำดับเลยครับ:

  1. (2,4) Trees
  2. 2-3-4 Trees and Red-Black Trees
  3. Deletion from Red-Black Trees

โดย Thep (noreply@blogger.com) ณ 18 September 2021 06:07 +0000

17 September 2021

bact

กรณีข้อมูลผู้ป่วยโรงพยาบาลเพชรบูรณ์หลุดรั่ว

ข้อมูลคนไข้ที่หลุดรั่วออกมา มีชื่อ-สกุลของคนไข้ วันเดือนปีเกิด เพศ ชื่อหมอ ค่าใช้จ่าย สถานะการจ่าย ประเภทสิทธิการรักษา ชื่อวอร์ด

ชื่อวอร์ดนี่ตั้งตามชื่อกลุ่มโรค ก็อาจเป็นข้อมูลอ่อนไหวได้

ประเภทสิทธิการรักษา บางประเภทบอกสถานะความพิการได้

ข้อมูลวันเดือนปีเกิด พอประกอบกับการที่หน่วยงานหลายแห่ง มักใช้วันเดือนปีเกิดเป็นรหัสผ่านตั้งต้น (ทั้งสำหรับลงทะเบียนเข้าระบบ หรือปลดล็อกเอกสาร PDF) มันก็เลยทำให้อันตราย

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

ข้อมูลเหล่านี้จำนวนนึงเป็นข้อมูลที่ฝังติดตัวมาตั้งแต่เกิดไม่สามารถเปลี่ยนแปลงได้ (เช่นวันเกิด) หลุดแล้วหลุดเลย อีกส่วนสามารถเปลี่ยนได้ แต่ยุ่งยาก เช่นชื่อสกุลนี่จริงๆ ก็เปลี่ยนได้ แต่ต้องวุ่นวายตามไปเปลี่ยนอีกหลายที่ ไม่เหมือนรหัสผ่านที่อย่างน้อยยังเปลี่ยนได้ง่ายๆ (ถ้าจำได้ว่าเคยไปตั้งรหัสนี้เอาไว้ที่ไหนบ้าง)

ชุดข้อมูลเหล่านี้ไม่จำเป็นจะต้องถูกใช้เดี่ยวๆ มันอาจเอาไปประกอบกับข้อมูลชุดอื่นที่หลุดมาก่อนหน้าหรือที่จะหลุดในอนาคตด้วยก็ได้

แม้จนถึงตอนนี้ พ.ร.บ.คุ้มครองข้อมูลส่วนบุคคล พ.ศ. 2562 จะถูก “เลื่อน” หรือยกเว้นไม่ให้ใช้บังคับอยู่เกือบทุกมาตราที่เกี่ยวกับการคุ้มครอง (เลื่อนมา 2 รอบละ) แต่มาตรา 4 วรรค 3 ยังไงก็ยังใช้บังคับอยู่นะ

“ผู้ควบคุมข้อมูลส่วนบุคคลตามวรรคหนึ่ง (2) (3) (4) (5) และ (6) และผู้ควบคุมข้อมูลส่วนบุคคลของหน่วยงานที่ได้รับยกเว้นตามที่กำหนดในพระราชกฤษฎีกาตามวรรคสอง ต้องจัดให้มีการรักษาความมั่นคงปลอดภัยของข้อมูลส่วนบุคคลให้เป็นไปตามมาตรฐานด้วย”

และน่าจะมีอย่างน้อยอีก 4 พ.ร.บ.ที่พอจะใช้ได้ ในมุมของผู้ได้รับผลกระทบ

1) โดยตัวข้อมูล เนื่องจากเป็นข้อมูลสุขภาพ ก็มี พ.ร.บ.สุขภาพแห่งชาติ พ.ศ. 2550 แต่ในรายละเอียดน่าจะมีข้อจำกัดอยู่มาก เนื่องจากไม่ได้ระบุเรื่องข้อมูลรั่วไหลตรงๆ แต่อาจจะไปพึ่ง “ธรรมนูญว่าด้วยระบบสุขภาพแห่งชาติ” อีกต่อหนึ่ง ซึ่งไกลอยู่

ข้อ 69 ในธรรมนูญว่าด้วยระบบสุขภาพแห่งชาติ พ.ศ. 2552 “ผู้บริโภคที่ได้รับความเสียหายจากการบริโภคสินค้าหรือการบริการต้องได้รับการชดเชยและเยียวยาอย่างมีประสิทธิภาพ เหมาะสม และรวดเร็ว”

ข้อ 1 (6) ในหมวดการคุ้มครองผู้บริโภคด้านสุขภาพ ของธรรมนูญว่าด้วยระบบสุขภาพแห่งชาติ ฉบับที่ 2 พ.ศ. 2559 “สิทธิในการร้องเรียนและสิทธิในการได้รับการชดเชยเยียวยาความเสียหายจากการบริโภค”

2) โดยตัวหน่วยงาน ถ้าเป็นโรงพยาบาลของกระทรวงเอง (ไม่ใช่ของมูลนิธิ ของบริษัท ของสภากาชาดไทย) ก็จะเป็นหน่วยงานรัฐ ซึ่ง พ.ร.บ.ข้อมูลข่าวสารของราชการ พ.ศ. 2540 ในมาตรา 23 ได้กำหนดหน้าที่ของหน่วยงานรัฐที่ดูแลข้อมูลอยู่

“หน่วยงานของรัฐต้องปฏิบัติเกี่ยวกับการจัดระบบข้อมูลข่าวสารส่วนบุคคลดังต่อไปนี้ จัดระบบรักษาความปลอดภัยให้แก่ระบบข้อมูลข่าวสารส่วนบุคคล ตามความเหมาะสม เพื่อป้องกันมิให้มีการนำไปใช้โดยไม่เหมาะสมหรือเป็นผลร้ายต่อเจ้าของข้อมูล […] ต้องแจ้งให้เจ้าของข้อมูลทราบในกรณีมีการจัดส่งข้อมูลข่าวสารส่วนบุคคลไปยังที่ใดซึ่งจะเป็นผลให้บุคคลทั่วไปทราบข้อมูลข่าวสารนั้นได้ […]”

3) ซึ่งพอเป็นหน่วยงานรัฐ ก็จะเข้าอีกกฎหมายคือ พ.ร.บ.การบริหารงานและการให้บริการภาครัฐผ่านระบบดิจิทัล พ.ศ. 2562 ในมาตรา 12

“เพื่อให้การบริหารงานและการให้บริการภาครัฐผ่านระบบดิจิทัลเป็นไป
ตามวัตถุประสงค์ตามมาตรา 4 […] ให้หน่วยงานของรัฐ […] ดำเนินการดังต่อไปนี้ให้เป็นไปตามธรรมาภิบาลข้อมูลภาครัฐตามมาตรา 8 จัดให้มีมาตรการหรือระบบรักษาความมั่นคงปลอดภัยในการเข้าสู่บริการดิจิทัลของหน่วยงานของรัฐ เพื่อให้มีความพร้อมใช้ น่าเชื่อถือ และสามารถตรวจสอบได้ โดยอย่างน้อยต้องจัดให้มี ระบบป้องกันหรือรับมือกับภัยคุกคามหรือความเสี่ยงทางไซเบอร์ตามกฎหมายว่าด้วยการรักษาความมั่นคงปลอดภัยไซเบอร์ […]”

(ดูประกาศคณะกรรมการพัฒนารัฐบาลดิจิทัล เรื่อง ธรรมาภิบาลข้อมูลภาครัฐ เพิ่ม)

  1. พ.ร.บ.การรักษาความมั่นคงปลอดภัยไซเบอร์ พ.ศ. 2562 ซึ่งถูกอ้างอิงจากกฎหมายอื่นๆ ข้างบน

ตอนนี้ขั้นต่ำสุดคือ โรงพยาบาลและกระทรวงต้องรีบแจ้งเจ้าของข้อมูล ว่าเกิดอะไรขึ้น ตัวเขาจะได้ระวัง เพื่อลดผลกระทบ

ไม่ต้องรอประเมินหรือพิสูจน์ความเสียหาย เรื่องพวกนั้นมันสำหรับขั้นต่อไปที่เกรงว่าข้อมูลจะถูกนำไปใช้ ขั้นนี้คือ ข้อมูลหลุดเป็นเรื่องที่เกิดสำเร็จแล้ว การแจ้งนี้อยู่ในวิสัยที่ทำได้ทันที กระทรวงบอกว่าได้ตรวจสอบรายชื่อแล้ว แปลว่ามีชื่อในมือแล้ว และโรงพยาบาลก็ควรมีช่องทางติดต่อกับคนไข้อยู่แล้ว โดยเฉพาะอย่างยิ่งสำหรับคนไข้ที่ใช้สิทธิการรักษาพยาบาลจากกองทุนต่างๆ

ช่วงที่ผ่านมามีกรณีหลุดรั่วแบบนี้เกิดขึ้นเรื่อยๆ กับข้อมูลสุขภาพ/สาธารณสุข เช่นกรณีเว็บไซต์ลงทะเบียนรับวัคซีนของชาวต่างชาติในประเทศไทย เปิดเผยข้อมูลผู้ลงทะเบียนกว่า 2 หมื่นคน มีทั้งชื่อ สกุล วันเกิด สัญชาติ ที่อยู่

หมอนวนรรน ธีระอัมพรพันธุ์ โพสต์ความเห็นส่วนตัวต่อเรื่องนี้ไว้ในเฟซ

โดย bact ณ 17 September 2021 20:47 +0000

16 September 2021

Vee

The advantages of various types of strings in Rust

New Rustacians usually ask why Rust has many types of strings. I don't know the original intention. Anyways, it has some advantages as follow:

  1. It minimizes the language core. Rust core has only str. String, OsString, CString are in the standard library, which is not a part of the language core.

  2. Programmer can control performance. For example, &str refers to a byte slice so it should be fast, while String is a wrapper of Vec. String should have more performance penalty from heap memory allocation.

  3. OsString and CString improve interoperability with other programming languages and operating systems. Rust can manipulate C-style strings directly without converting them to native Rust strings. OsString is also similar.

String conversation has a performance penalty. Also sometimes string conversation is not obvious. Some systems don't even use Unicode or other standards.

The alternative of having too many types is treating everything as a byte slice. However, type checking doesn't work well with too few types.

โดย Vee Satayamas ณ 16 September 2021 06:08 +0000

6 September 2021

bact

สภากาชาดไทย – หน่วยงานที่รัฐให้อำนาจมาก แต่มีกลไกกำกับน้อย?

สภากาชาดไทยเป็นหน่วยงานรัฐหรือไม่?

สภากาชาดไทยนี่ก็เป็นอีกองค์กรที่สถานะงงๆ ในเว็บไซต์บอกว่าเป็นองค์กรการกุศล (ทำนองสมาคม เอ็นจีโอ) งบประมาณจำนวนมากมาจากงบประมาณแผ่นดิน (ซึ่งก็มาจากภาษี เงินรายได้ เงินกู้ ฯลฯ) ส่วนสภานายก/นายิกาและอุปนายกผู้อำนวยการ กษัตริย์เป็นผู้แต่งตั้งโดยตรง

ในแง่การได้รับงบประมาณจากรัฐ (ไม่ว่าสถานะหน่วยงานจะเป็นอะไรก็ตาม) เรื่องนี้ไม่ได้แปลก เพราะก็มีหลายหน่วยงานได้รับอุดหนุนในลักษณะนี้ รวมไปถึงวัดต่างๆ ด้วย และแม้งบประมาณที่สภากาชาดไทยจะได้รับเป็นตัวเงินที่สูงมาก (ปี 63 ได้รับจัดสรร 10,651 ล้านบาท ปี 64 ได้ 8,872 ล้าน) คิดว่าหลายคนก็คงมองว่า ก็ไม่น่ามีปัญหาอะไร เพราะภารกิจที่ทำก็เป็นสิ่งสาธารณะได้ประโยชน์

แต่ก็มีคำถามในแง่ว่า แล้วในฐานะผู้อุดหนุนงบประมาณ ตัวรัฐบาลสามารถมีส่วนร่วมในการกำหนดนโยบายการดำเนินงานของสภากาชาดได้มากน้อยแค่ไหน หรือกระบวนการสรรหาผู้บริหารและการบริหารของสภากาชาด มันรับผิดรับชอบกับเจ้าของเงินมากน้อยแค่ไหน

เพราะบางทีก็ดูเหมือนว่าสภากาชาดจะได้รับอำนาจเหมือนหน่วยงานรัฐ แต่พอมีกรณีพิพาท ก็เหมือนกับว่าสภากาชาดจะไม่ต้องรับผิดรับชอบภายใต้กลไกเดียวกันกับหน่วยงานรัฐ

เคยมีคนจะขอข้อมูลจากโรงพยาบาลจุฬาลงกรณ์ โดยใช้พ.ร.บ.ข้อมูลข่าวสารของราชการ พ.ศ. 2540 ซึ่งคณะกรรมการวินิจฉัยการเปิดเผยข้อมูลข่าวสาร สาขาสังคม การบริหารราชการแผ่นดินและการบังคับใช้กฎหมาย มีคำสั่งที่ สค 235/2558 (12 พ.ย. 2558) วินิจฉัยว่าสภากาชาดไทย (ซึ่งเป็นนิติบุคคลบุคคลที่ดำเนินงานโรงพยาบาลจุฬาฯ) ไม่ใช่หน่วยงานของรัฐตามพ.ร.บ.ข้อมูลข่าวสาร ดังนั้นจึงขอข้อมูลไม่ได้

ในคำวินิจฉัยของคณะกรรมการวินิจฉัยฯ มีอ้างถึงคำสั่งศาลปกครองสูงสุดที่ว่าสภากาชาดไทย “มีลักษณะเป็นองค์การอาสาสงเคราะห์ ไม่ใช่หน่วยงานทางปกครอง” ดังนั้นเจ้าหน้าที่ของศูนย์บริการโลหิตแห่งชาติไม่ใช่เจ้าหน้าที่ของรัฐตามพ.ร.บ.จัดตั้งศาลปกครอง

นี่ก็เพิ่งรู้ เท่ากับว่าสำหรับโรงพยาบาลจุฬาลงกรณ์ ศูนย์บริการโลหิตแห่งชาติ ศูนย์รับบริจาคอวัยวะ อะไรก็ตามที่อยู่ภายใต้สภากาชาดไทย เราไม่สามารถเข้าถึงข้อมูลตามพ.ร.บ.ข้อมูลข่าวสารของราชการเพื่อตรวจสอบการทำงานได้เลย รวมถึงไม่สามารถใช้กลไกทางปกครองมาคุ้มครองได้

ยังมีอีกที่แก้ไขพ.ร.บ.ว่าด้วยสภากาชาดไทยล่าสุด ยกเว้นให้สภากาชาดไม่ต้องอยู่ภายใต้กฎหมายคุ้มครองแรงงาน แรงงานสัมพันธ์ ประกันสังคม ซึ่งแพทเทิร์นการเขียนแบบนี้มันเหมือนพวกองค์การมหาชนทั้งหลายเลย เท่ากับคนทำงานในสภากาชาดนี่ ไม่ใช่ทั้งพนักงานรัฐ แต่ก็ไม่ได้รับการคุ้มครองแบบพนักงานเอกชน (ในมุมคนทำงานที่สภากาชาด สภาพปัจจุบันก็อาจจะโอเคก็ได้ เพราะมีสวัสดิการนั่นนี่ตามที่หน่วยงานจะจัดให้ ซึ่งเผลอๆ ดีกว่าหลายที่ แต่ถ้าจะเกิดไม่พอใจ จะฟ้องร้อง เครื่องมือทางกฎหมายก็หายไปเยอะมาก)

อย่างศูนย์โลหิตแห่งชาตินี่ ถ้าจะให้มีสถานะ “แห่งชาติ” เป็นศูนย์กลางในเรื่องนี้ของทั้งประเทศ มีอำนาจตัดสินใจให้เกิดผลได้เสียกับประชาชนและหน่วยงานอื่นๆ ก็ควรจะทำให้มันมาอยู่ในกลไกที่ใช้กฎหมายปกครองถ่วงดุล คุ้มครอง และเยียวยาได้ หรือใช้พ.ร.บ.ข้อมูลข่าวสารเข้าถึงสิทธิที่ประชาชนควรมีได้ตามปกติรึเปล่า

คืองานกาชาดและงานบริการเลือดมันเริ่มจากบริบทสงครามก็จริง (กรณีงานบริการเลือดคือสงครามกลางเมืองสเปน) แต่พอหลังสงคราม ในหลายประเทศก็คลี่คลาย เอางานบริการเลือดนี้ไปเป็นงานสาธารณสุขในภาวะปกติที่รัฐดูแลจัดการ ไม่ได้เป็นเรื่องภาวะพิเศษอีกต่อไปแล้ว เช่นงานเลือดและอวัยวะระดับชาติของสหราชอาณาจักร NHS Blood and Transpant จะเป็นผู้ดูแล ไม่ใช่ British Red Cross ซึ่งพอเป็น NHS มันก็เข้ากลไกร้องเรียน เยียวยา ตามปกติของหน่วยงานรัฐ รวมถึงในแง่การดำเนินนโยบาย มันก็จะประสานไปในทางเดียวกันกับ NHS หลัก — แต่ของไทย งานบริการเลือดของเรายังอยู่กับกาชาด เหมือนสมัยสงครามอยู่จริงๆ

คืองานบริการเลือด งานรับบริจาคอวัยวะ จะอยู่กับกาชาดต่อก็ไม่ได้มีปัญหาอะไร ตราบใดที่กลไกคุ้มครองมันมีพอกันกับหน่วยงานบริการสาธารณะอื่นๆ

คำถามรวมๆ ก็คือ ทำไม power กับ accountability นี่มันไม่มาด้วยกัน

มีคนทำวิทยานิพนธ์เรื่องนี้ด้วย จากมุมกฎหมายมหาชน

อัครพล อร่ามเจริญ. สถานะทางกฎหมายของสภากาชาดไทย. วิทยานิพนธ์มหาบัณฑิต คณะนิติศาสตร์ มหาวิทยาลัย ธรรมศาสตร์, 2561. https://digital.library.tu.ac.th/tu_dc/frontend/Info/item/dc:165987 (ฉบับย่อเป็นบทความ ในวารสารบัณฑิตศึกษานิติศาสตร์ ปีที่ 12 ฉบับที่ 4 เดือน ตุลาคม – ธันวาคม 2562PDF)

กลุ่มองค์กรที่ใช้เงินรัฐ ใช้การอำนวยความสะดวกจากอำนาจรัฐ บุคลากรของรัฐ และทรัพยากรของรัฐ แต่ไม่อยู่ภายใต้ความรับผิดรับชอบแบบที่องค์กรรัฐปกติต้องมี นี่น่าจะมีอีกเยอะ (ราชวิทยาลัยจุฬาภรณ์?) และน่าจะมีไปทั่วทั้งส่วนกลาง ภูมิภาค และท้องถิ่น

เช่นกรณีกรมส่งเสริมการปกครองท้องถิ่น กระทรวงมหาดไทย มีหนังสือด่วนที่สุด มท 0808.2/ว5426 (24 ก.ย. 2561) “ซักซ้อมแนวปฏิบัติ” กับผู้ว่าราชการจังหวัดทั่วประเทศในการตั้งงบประมาณเงินอุดหนุนองค์กรปกครองส่วนท้องถิ่นเพื่อสนับสนุนภารกิจของกาชาดจังหวัด

ในมุมการกระจายอำนาจ การที่ส่วนกลางมีหนังสือให้ส่วนภูมิภาค ไปซักซ้อมกับส่วนท้องถิ่นถึงการตั้งงบประมาณ เจาะจงให้ไปสนับสนุนงานของมูลนิธิอะไรเป็นการเฉพาะนี่ก็น่าตั้งคำถามอยู่ ว่ามันเหมาะสมหรือเปล่า คือถ้าท้องถิ่นเขาจะให้เองนี่ก็เรื่องของเขา แต่ส่วนกลางไปยุ่งแบบนี้ได้เหรอ (แต่ในหนังสือก็มีความระมัดระวังในการใช้คำ เข้าใจว่าก็รู้ว่าไม่มีอำนาจ เลยเลี่ยงไปใช้คำว่า ซักซ้อม แนวปฏิบัติ เปิดช่องไว้ว่า จริงๆ ไม่ต้องทำก็ได้นะ ดังนั้นไม่ใช่คำสั่ง)

คำสั่งคณะกรรมการวินิจฉัยการเปิดเผยข้อมูลข่าวสาร สาขาสังคม การบริหารราชการแผ่นดินและการบังคับใช้กฎหมาย ที่ สค 235/2558 เรื่อง อุทธรณ์คำสั่งมิให้เปิดเผยข้อมูลข่าวสารเกี่ยวกับประวัติการรักษาพยาบาล (12 พ.ย. 2558) http://www.oic.go.th/FILEWEB/CABINFOCENTEROPM/DRAWER01/GENERAL/DATA0004/00004684.PDF

คำสั่งศาลปกครองสูงสุด ที่ 585/2554 ระหว่าง นาย ส. ผู้ฟ้องคดี และเลขาธิการสภากาชาดไทย ที่ 1 อุปนายกผู้อํานวยการสภากาชาดไทย ที่ 2 ผู้ถูกฟ้องคดี https://www.krisdika.go.th/data/article77/filenew/585-2554.pdf

ที่ มท 0808.2/ว5426 เรื่อง ซักซ้อมแนวปฏิบัติการตั้งงบประมาณและการเบิกจ่ายเงินอุดหนุนขององค์กรปกครองส่วนท้องถิ่นเพื่อสนับสนุนภารกิจของเหล่ากาชาดจังหวัด (24 ก.ย. 2561) http://www.dla.go.th/upload/document/type2/2020/12/24704_3_1608885882330.pdf

โพสต์ครั้งแรกในเฟซบุ๊ก 2 ก.ย. 2564

โดย bact ณ 6 September 2021 15:13 +0000

5 September 2021

Vee

Key-value data in Common Lisp

I enjoy using key-value data in dynamic languages. For example, in Python, I can create key-value data for storing the metadata of a document as shown below. I don't discuss why I don't use struct, class, named tuple in this post.

doc_metadata = {"title": "The Rust Programming Language",  
                             "type": "book", 
                             "number-of-pages": 584, 
                             "authors": ["Steve Klabnik", 
                                                "Carol Nichols", 
                                                "contributions"]}

I can code read/write a value easily, for example:

# Write
doc_metadata["type"] = "text book"

# Read
print(doc_metadata["type"])

In Perl and Ruby, we can use Hash, which is almost the same thing as Dict in Python. In JavaScript, we can use an object.

Common Lisp is different. We can use a hash table, but it is not as convenient as Dict in Python.

(let ((doc-metadata (make-hash-table)))
  (setf (gethash :title doc-metadata) "The Rust Programming Language")
  (setf (gethash :type doc-metadata) :BOOK)
  (setf (gethash :number-of-pages doc-metadata) 584)
  (setf (gethash :authors doc-metadata) '("Steve Klabnik"
                                                                      "Carol Nichols" 
                                                                      "contributions")))

Besides construction, printing a hash table is not so convenient. Maybe one can create a function or macro to make creating/printing a hash table convenient. I still felt that I abused Common Lisp.

My code is usually too buggy when I keep mutating the same variable. So I prefer using an immutable data structure to prevent me from messing things up. Moreover, my key-value data usually do not have more than five keys. So I don't strictly need to use an efficient data structure, namely, hash table or binary search tree. So I use alist (assosiation list). I can construct a list like below:

(setq doc-metadata '((:title . "The Rust Programming Language")
                     (:type . :BOOK)
                     (:number-of-pages . 542) 
                     (:authors . '("Steve Klabnik"
                                   "Carol Nichols" 
                                   "contributions"))))

IMO, it looks concise and convenient. We can retrieve key-value pair with a specific key using the assoc function, which I suppose it does linear search. Linear search can be slow. However, my alist doesn't have a lot of keys.

Instead of replacing a value with another value, I can add a new key-value pair with an existing key, for example:

(setq another-doc-metadata (acons :type :TEXT-BOOK doc-metadata))

By retrieving the value of :type using assoc, we get the new value because assoc function retrieves the first key found in alist, for example:

(cdr (assoc :type another-doc-metadata))
;; OUTPUT => :TEXT-BOOK

However, with function calls instead of number/string literal, alist doesn't look concise anymore, for example:

(list (cons :title (get-title x y z))
       (cons :type (get-type x))
       (cons :number-of-pages (get-number-of-pages a b c)) 
       (cons :authors (get-authors c d)))

plist looks much more concise, for example:

(setq doc-metadata (list :title (get-title x y z)
       :type (get-type x)
       :number-of-pages (get-number-of-pages a b c) 
       :authors (get-authors c d)))

I can retrieve a value corresponding to a key easily by getf function. For example:

(getf doc-metadata :type)

A new value can be replaced the old value by setf, example:

(setf (getf doc-mentadata :type) :TEXT-BOOK)

setf is different from acons since acons doesn't mutate the existing list, setf does. Therefore plist is not exactly what I'm looking for.

Maybe the best way is using an Alexandria function for converting plist ot alist as Michał "phoe" Herda suggested.

โดย Vee Satayamas ณ 5 September 2021 16:54 +0000