ท่าดัก error ในภาษา Go กับ C

เวลาเขียน Go แล้วเจอ

if err != nil {

}

นี้เราอาจจะคิดว่ามันน่าเบื่อ มันอาจจะคล้าย ๆ กับ try: except: เวลาเขียน Python แต่ว่า ปกติแล้ว ถ้ามี exception เกิดขึ้น Python มันจะหยุดทำงานใน flow ปกติไปเลย ยกเว้นเราจะระบุไว้ไปเป็นอย่างอื่น แต่ Go นี่ถ้าไม่ระบุอะไร เช่นบอกว่าให้ return หรือ panic มันก็จะทำงานของมันต่อไปเรื่อย ๆ

แต่พอมาดูกรณีภาษา C ที่พี่ bact ยกมาเมื่อวาน ซัดกับ iconv จาก issue นี้

iconv มัน return size_t ออกมาซึ่งจริงๆ แล้ว เป็น unsigned integer แต่ว่าถ้ามี error มันจะ return -1 ที่ถูก cast เป็น size_t แล้วออกมา เวลาจะตรวจสอบว่ามี error หรือเปล่าต้องทำแบบนี้

if (res != (size_t) -1) {
   
}

มองในแง่นี้ Go ก็ดูดีขึ้นมาทันที อาจจะเขียนเยอะไปบ้าง แต่ก็ทำอะไรชัดเจนตรงไปตรงมาดี ถึงแม้บางทีผมจะรู้สึกว่าน่าเบื่อก็ตาม

lazy-seq #Clojure

เรื่องนี้ออกตัวก่อนว่าจริง ๆ ผมก็ยังงง ๆ อยู่ แต่เขียนบันทึกสิ่งที่คิด ๆ ไว้ก่อน

เมื่อวานผมอ่าน lazy sequence มาจากไฟล์แล้วเอามา process ต่อ loop-recur ใน loop recur ก็เอาผลลัพธ์มาต่อ ๆ กันด้วย cons รู้สึกแบบนี้มันไม่ค่อยมีประโยชน์เพราะว่า ผลลัพธ์ทั้งหมดก็จะอยู่ใน ram หมดเลย

ก็เลยคิดว่าน่าจะทำเป็น lazy sequence ได้ แต่พอไม่ใช่ filter map เรื่องมันก็เริ่มจะไม่ตรงไปตรงมาแล้ว ผมพยายามจะลดรูปปัญหาให้ง่าย ๆ อย่างนี้ มีไฟล์ csv ประมาณนี้โดยที่เรียง (sort) ตาม column แรกไว้

1,cat
1,man
1,can
3,ruby
3,prolog
4,perl

สิ่งที่ต้องการคือถ้ามี id ซ้ำกันเอาแค่ row แรกพอ เช่นจากด้านบนผลที่จะได้คือ [[1 cat] [3 ruby] [4 perl]] แต่ต้องจิตนาการว่าไฟล์ใหญ่สัก 20GB อะไรแบบนี้นะครับโหลดขึ้น ram หมดไม่ได้

อ่านไปอ่านมาก็พอว่าต้องใช้ macro ชื่อ lazy-seq พอเขียน code ก็ออกมาได้อย่างนี้

(defn distinct-id [rows]
  (lazy-seq
   (when-let [s (seq rows)]
     (let [row (first s)]
       (cons row
             (distinct-id 
                (drop-while
                     #(= (first row) (first %))
                     (rest s))))))))

ไม่รู้ว่ามันใช้ได้จริงหรือเปล่าเลยลองยัด data ที่มันไม่สิ้นสุดใส่เข้าไป

(take 3 (distinct-id (map list (range) (range))))

ถ้าหากว่ามันไม่ lazy จะ stack overflow เลย เพราะมันหาไปเรื่อยไม่เสร็จสักที แต่ปรากฎว่าก็ใช้ได้

เพื่อความชัวร์ก็ลองเอา lazy-seq ออก

(defn distinct-id [rows]
  ;;(lazy-seq
   (when-let [s (seq rows)]
     (let [row (first s)]
       (cons row
             (distinct-id 
                 (drop-while 
                     #(= (first row) (first %))
                     (rest s)))))))

แล้วรัน (take 3 (distinct-id (map list (range) (range)))) อีกทีก็บึ้มตามคาด

when-let นี้ผมไม่รู้เอามาจากไหนก็เลยลองเอาออกดู แก้เป็นแบบนี้

(defn distinct-id [rows]
  (lazy-seq
   (when rows
     (let [row (first rows)]
       (cons row
             (distinct-id 
                 (drop-while 
                     #(= (first row) (first %))
                     (rest rows))))))))

 

ก็ทำงานได้ปกติ

ตอนนี้ตามที่เข้าใจคือ lazy-seq มันจะสร้าง object มาหุ้ม body คือ

 

 ;; body
 (when rows
     (let [row (first rows)]
       (cons row
             (distinct-id 
               (drop-while 
                   #(= (first row) (first %))
                   (rest rows))))))

 

ไว้

พอมีการเรียกใช้งาน เช่น (first (distinct-id (map list (range) (range)))) มันก็จะมาเรียกใช้ body 1 ที แล้ว body มันจะคืน sequence หรือจริง ๆ แล้วก็คือ Cons ออกไป โดยจะมี 2 ส่วนคือส่วน first ได้แก่ row ที่คำนวณเสร็จเรียบร้อยแล้ว และ rest ที่เป็น lazy-seq อีกตัว ประมาณนี้

(cons row (lazy-seq …))

เพราะว่าส่วนหลัง (rest) มันเป็น lazy sequence อยู่ก็เลยยังไม่ได้คำนวณจริง ๆ ก็เลยทำให้โปรแกรมมันกลายเป็นแบบ lazy ไปแล้ว (take 3 (distinct-id (map list (range) (range)))) มันก็ทำงานได้โดยเรียก body แค่ 3 ที

Counting if err != nil in source codes written in Go

I wonder whether if err != nil is actually use in source code of a popular project written in Go programming language.

So I obtained runc including libcontainer from github:

git clone https://github.com/opencontainers/runc.git

I counted these following things:

  1. Number of source code files
    $ find runc -name '*.go' | wc -l
    359
    
  2. Number of lines
    $ find runc -name '*.go' | xargs -I '{}' cat '{}' | wc -l
    61209
    
  3. Number of “if err”
    $ find runc -name '*.go' | xargs -I '{}' cat '{}' | grep 'if err' | wc -l
    1684
    
  4. And finally, number of “if err != nil”
    $ find runc -name '*.go' | xargs -I '{}' cat '{}' | grep 'if err != nil' | wc -l
    1006
    

1006 of “if err != nil” from 359 files with 61209 lines. So I suppose that using if err != nil in Go-based project is common.

Range ใน Javascript

Range นี้ผมใช้ตอนอยากจะดูว่า ข้อความ อะไรที่ถูกเลือกอยู่บ้าง เวลาใช้ก็เขียนแบบนี้

var sel = window.getSelection();
if(sel.rangeCount > 0) 
    var r = sel.getRangeAt(0);

เขียน code แบบนี้ก็จะได้ r ที่เป็น range ออกมา อ่อผมใช้กับ Firefox 3.0.x และ Opera 10.x นะครับ IE 6-8 เข้าใจว่าใช้ไม่ได้ ส่วน chrome กับ safari ก็ยังไม่ได้ศึกษาเท่าไหร่

พอได้ range มา ผมก็รู้ได้ว่า element ไหน โดนเลือกตัวเริ่ม ตัวไหนตัวปิดโดยดูจาก r.startContainer และ r.endContainer ตามลำดับ แต่ว่าใน container อาจจะเป็น Text ก็อาจจะถูกเลือกแค่บางตัวในนั้นก็ต้องมาดู r.startOffset และ r.endOffset อีก

เท่านี้ก็คงพอใช้งานได้แล้ว …

Mockito: when … thenReturn แบบปริยาย และมีข้อยกเว้น

ผมอยากเขียน mock แบบได้ค่าอะไรมาก็ตามก็ให้ return 30; ไป แต่ถ้ารับ 20 มาให้ return 20; ไปก็เลยมาลองๆ ทำดูมั่วๆ ใช้ไม่ค่อยเป็นด้วย

package funfun;
import static org.mockito.Mockito.*;

public class Moki {
	public int foo(int x) {
		return 10;
	}

	public static void main(String[] args) {
		Moki ki = mock(Moki.class);
		when(ki.foo(anyInt())).thenReturn(30); // ปริยาย
		when(ki.foo(20)).thenReturn(20); // ยกเว้น
		System.out.println(ki.foo(20));
		System.out.println(ki.foo(30));
	}
}

ก็เลยเขียนได้ความแบบข้างบน พบว่าต้องเอาค่าปริยายขึ้นก่อน แล้วข้อยกเว้นตามทีหลัง

CakePHP ออก 1.2 RC1 แล้ว

CakePHP ออก 1.2 RC1 แล้ว หลังจากเป็น Beta อยู่นานเหมือนกัน. และตามมาด้วยต้องออกแรงกันนิดหน่อยเพื่อที่จะ migrate จาก beta ไป rc1 ดูได้ที่ http://cakebaker.42dh.com/2008/06/05/migrating-from-cakephp-12beta-to-rc1/

ช่วงนี้ผมคงยังไม่ได้เขียน web แต่เขียน blog ไว้ก่อนกันลืม😛