Typeclass เทียบกับ Java

พอดู typeclass กับ parametric polymorphism ใน Haskell แล้วก็คิดว่า มันก็คล้าย ๆ Generic Methods and Bounded Type Parameters ใน Java หรือเปล่า
 
 
public static <T extends Comparable<T>> int countGreaterThan(T[] anArray, T elem) {
 
 
ตรง extends Comparable<T> ก็คล้าย ๆ เอา typeclass มาใส่เป็น constraint
 
แต่ประมาณว่าถ้ามันพลังเท่า ๆ กันก็น่าจะ define monad แบบใน Haskell ด้วย Java ได้ ก็เลยไปหาดูเจอ https://stackoverflow.com/questions/35951818/why-can-the-monad-interface-not-be-declared-in-java ก็พบว่าไม่ได้ ส่วนที่พอจะจับใจความได้คือ:
no ability to specify that static members are part of the interface contract
สรุปว่าก็เกือบ ๆ จะพลังเท่ากัน แต่ไม่
Advertisements

GC ของ JVM จาก Redhat ที่แก้ปัญหา pause นาน

มิตรสหายหลายท่านเล่าให้ฟังว่า Java มันไม่ได้มีแค่ปัญหากิน RAM แต่ว่ามีปัญหาว่า GC มันพา pause นานด้วย ตอนที่ pause นี่ก็ทำงานไม่ได้ไปเลย

ท่าแก้แบบกำปั้นทุบดินสุด ๆ ก็คือทำ GC ใหม่

ใน Table 3 ผมเข้าใจว่าเขาลองกับ ElasticSearch

Max Pause ของ Parallel (GC) 3.04s แต่งานนี้ลดลงมาได้เหลือ 89.79ms ส่วนค่าเฉลี่ยก็ลดจาก 823.75ms มาเหลือ 53.01 ms

ที่น่าสังเกตคือเป็นงาน Redhat ที่ทำบน OpenJDK ไม่ใช่ของ Oracle ทำให้คิดว่าช่วงนี้อาจจะเป็นจุดเปลี่ยนสำคัญของ Java เหมือนกัน

https://dl.acm.org/citation.cfm?id=2972210

docx กับ odt

ไฟล์เอกสารยุคนี้ทั้ง docx และ odt ข้างในเป็น XML ที่เอามา zip ไว้ ผมอยากจะเปิดไฟล์มีดูว่ามีส่วนไหนที่ถูกขีดเส้นใต้บ้าง โดยที่ใช้ XML parser เท่านั้นเองจะทำอย่างไร

ผมเลยลองสร้างเอกสารขึ้นมาหน้าตาแบบข้างล่าง:

animal_libre

ซึ่งขีดเส้นใต้คำว่า “Dog” ไว้

พอแตก .docx ออกมาเข้างในจะมี word/document.xml

ข้างล่างผมดึงมาเฉพาะส่วนที่หุ้ม “Dog” อยู่

docx_u

ความประทับใจเกี่ยวกับ .docx คือ ตัวย่อเยอะมาก เช่น rPr ilvl คือคิตตี้อะไร แต่จริงๆ สามารถเปิดดูจากมาตรฐาน ISO 29500 ได้ แต่โดยสรุปแล้วส่วนที่ขีดเส้นใต้ก็คือ w:u แต่ไม่ได้หุ้ม Dog อยู่แต่ไปอยู่ใน node w:rPr ข้าง ๆ อีกที

ต่อไปเป็น .odt พอแตกออกมา ไฟล์หลักก็คือ content.xml:
odt_xml.png

ส่วนที่หุ้ม Dog อยู่ก็คือส่วนที่ highlight สีเหลือง ๆ ไว้จะเห็นว่า ชื่อ tag ชื่อ attribute เขียนมาเต็ม ๆ แทบจะไม่มีอะไรให้เดา แต่ว่าส่วนที่บอกที่ขีดเส้นใต้ไปอยู่ใน style P2 อีกที ซึ่งก็อยู่ในไฟล์เดียวกัน

odt_underline

อันนี้ชื่อก็บอกชัดเจน style:text-underline-style=”solid” บอกว่าขีดเส้นใต้เป็นเส้นทึบ

เวลาเขียนโปรแกรมสำหรับ odt ก็ประมาณข้างล่างนี้ครับ


require 'pp'
require 'zip'
require 'rexml/document'

def get_attrs(style_node)
  style_node.get_elements('.//style:text-properties')
    .map{|text_prop_node| text_prop_node["style:text-underline-style"] == "solid" \
         ? :underline : nil}
    .flatten
    .reject(&:nil?)
end

Zip::File.open('animal.odt') do |zipfile|
  content_xml = zipfile.select{|entry| entry.name == "content.xml"}[0]
                  .get_input_stream
                  .read
  doc = REXML::Document.new(content_xml)
  style_map = Hash[doc.root
                     .get_elements('//style:style')
                     .map{|node| [node['style:name'], get_attrs(node)]}]
  PP.pp doc.root
          .get_elements('//text:p')
          .map{|node| {text: node.text,
                       style: style_map[node['text:style-name']]}}
end

ใช้ rubyzip แตกไฟล์ content.xml ออกมาแล้ว แล้วก็หาว่า style ไหนขีดเส้นใต้บ้างแล้วใส่ไว้ใน style_map เสร็จแล้วก็มาไล่ดู text:p ทีละอัน

ผลตอนรันก็จะออกมาทำนองนี้


[{:text=>"Animal", :style=>nil},
{:text=>"Cat", :style=>[]},
{:text=>"Rat", :style=>[]},
{:text=>"Dog", :style=>[:underline]},
{:text=>"Elephant", :style=>[]},
{:text=>"Bird", :style=>[]}]

ส่วน docx ก็น่าจะอ่านได้คล้าย ๆ กัน

เพระาว่า .odt และ .docx เป็น xml ที่เอาไป zip ไว้ แค่ lib สำหรับ parse xml และหาด้วย xpath กับ lib ไว้แตก zip ใช้งาน odt กับ docx ได้แล้ว บางทีอาจจะง่ายกว่าใช้ lib สำหรับอ่านไฟล์เอกสารโดยเฉพาะอีกก็ได้

น้ำท่วมกับทวิตเตอร์

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

สรุปว่าคนอยุธยาไม่ค่อยใช้ทวิตเตอร์ ?

เตรียมเทียบความเร็วการตัดคำแบบใช้ multi-core

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

ก็เลยมีคนทำตัวเทียบออกมา

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

โปรแกรมตัดคำเคยเทียบไปรอบนึงแล้วแต่ลองบน single core แถม corpus ก็ใช้ตัวไหนแล้วก็ไม่รู้ ลืม! แล้วก็รู้สึกจะเป็นตัวที่แจกไม่ได้ เครื่องที่ test ก็เป็น laptop ของผมเองเวลารันก็ไม่สะดวกเพราะว่าต้องหยุดทำงาน

ในรอบใหม่นี้ก็เลยคิดมาได้ 2 อย่างคือ

  1. ต้องใช้ corpus ที่มันแจกได้ ก็เลยจัดมา 2 ตัวคือวิกิพีเดียไทยที่มีสัญญาอนุญาตครีเอทีฟคอมมอนส์แบบแสดงที่มา-อนุญาตแบบเดียวกัน และพระไตรปิฏกภาษาไทยฉบับหลวงซึ่งเก่าแล้วลิขสิทธิ์หมดอายุ ทั้งหมดโหลดได้ที่ http://file.veer66.rocks/langbench/
    วิกิพีเดียวิธีสร้างมาประมาณนี้

    โหลดก่อน

    wget https://dumps.wikimedia.org/other/cirrussearch/20171009/thwiki-20171009-cirrussearch-content.json.gz -O - | zcat > thwiki-20171009-cirrussearch-content.json

    แล้วก็เอา plain text ออกมา

    require 'json'
    
    File.open('thwiki-20171009-cirrussearch-content.json') do |f|
      f.each_line do |line|
        data = JSON.parse(line)
        if data['title'] and data['text']
          puts data['title']      
          puts "=" * data['title'].length
          puts
          puts data['opening_text'] if data['opening_text']
          puts
          puts data['text']
          puts
          puts data['auxiliary_text'].join("\n") if data['auxiliary_text']
          puts
          puts
        end
      end
    end
    
    

    ส่วนพระไตรปิฎกโหลดจาก http://download.watnapahpong.org/data/E-Tipitaka-latest.tar.gz และก็มาสั่ง SQLite แบบนี้


    echo 'select content from main;' | sqlite3 thai.db | bzip2 > ~/Develop/corpus/thai-etipitaka.txt.bz2

  2. ส่วน H/W จะใช้ Raspberry Pi 3 Model B ที่เสียบ MicroSD (HC) Class 10 ของ Sandisk (Ultra)

    rpi3.jpg

    ที่ใช้ rpi3 เพราะมันมี 4 core; RAM ไม่เยอะเกินไปไม่งั้นมันจะเขียนง่ายไป; เครื่องไม่แพงมากหาง่ายด้วย คนอื่นซึ้อมาทดลองตามได้ ส่วนใช้ OS อะไรยังเลือกไม่ได้ครับ ตัด NetBSD ออกไปก่อนเพราะ netbsd-evbarm target ของ Rust ยังไม่มี ก็น่าจะใช้ GNU/Linux แต่ไม่แน่ใจว่า distro ไหน

ส่วนกติกาก็คือว่าทุกตัวจะใช้ algorithm เดียวกัน จะปรับเฉพาะวิธีเขียนโปรแกรมเท่านั้น

สิ่งที่ขาดไปและควรจะมีมากคือตัวทดสอบว่าผลการตัดคำมันออกมาถูกตาม algorithm ที่วางไว้หรือเปล่า ถ้าไม่มีใน wordlist หรือกำกวมก็แล้วไป ประมาณว่าผิดก็ควรจะผิดเหมือนกันทุกตัว ถูกก็ถูกเหมือนกันทุกตัว ผมคิดว่า sample ออกมาสัก 2 บรรทัดก็พอ ที่เหลือก็ดูว่า output ออกมาจำนวนบรรทัดเท่ากันก็น่าจะพอแล้ว มั้ง

ถ้าหากท่านใดสนใจก็สามารถโหลด source code ภาษาที่ท่านชอบแล้วก็ลองโมลอง optimize กันได้เลยครับ แล้ว pull request เข้ามา ในอนาคตถ้ามีคนสนใจมาก ๆ ผมอาจจะเขียน script ไว้ให้รันทุกคืนไปเลย ส่วน source code เอาได้จาก link ที่ http://veer66.rocks/2017/06/04/thai-word-break-in-various-programming-languages.html ครับ

ส่วนเวลาทำสอบผมก็จะแตก bzip2 ก่อนนะครับ แล้วครับมารัน


time ./wordcut thai-wtipitaka.out.txt

ถ้าพวกที่ใช้ JVM ก็จะรันทำนองนี้


time java -jar wordcut.jar thai-wtipitaka.out.txt

Rich Hickey พูดถึง RDF ด้วย

Rich Hickey เป็นบิดาของภาษา Clojure ซึ่งอาจจะเรียกได้ว่าสวนกระแสปี 2017 ก็ได้ เพราะว่าภาษาส่วนมากจะออกไปทาง static typing + function programming แบบนี้ แต่ Clojure เน้น dynamic typing แต่จะเขียน spec ใส่ก็ได้เป็น option

ตอนที่ RDF ออกมาใหม่ ๆ ผมรู้สึกว่าจะทำออกมาทำไม ใช้ database ธรรมดา ๆ แบบ PostgreSQL แทนก็ได้ จะแลกเปลี่ยนข้อมูลก็ใช้ CSV สิงี้

Screenshot-2017-10-14 Opening Keynote - Rich Hickey - YouTube.png

ใน video เขาพูดได้เห็นภาพมาก ๆ พอมี scheme มี table ของตัวเองแต่ละองค์กรมันก็ไม่ค่อยจะเหมือนกันหรอก เวลาจะเอามาใช้งานร่วมกันก็ต้องมานั่งแปลงก็รากแตกรากแตนอยู่ดี

ตัวย่อชื่อภาษา

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

ถ้าต้องย่อมีมาตรฐานตัวต่ออยู่คือ ISO 639-3 ภาษาญี่ปุ่นใช้ JPN ครับ ไทยใช้ THA อังกฤษใช้ ENG

https://en.wikipedia.org/wiki/Jap

Parallel processing แบบง่าย ๆ ใน Ruby

ขั้นแรกเลยคือลง gem นี้ก่อน Parallel แบบชื่อตรงมาก พอลงแล้วเขียนอะไรประมาณนี้ได้เลย


require 'parallel'
a_mul_10 = Parallel.map([1,2,3,4,5]) {|elem| elem + 10}

หรือถ้าอยากให้มันเยอะ ๆ หน่อยจะได้ top ดูทันว่าใช้กี่ core แล้วก็สั่งแบบ


require 'parallel'
a_mul_10 = Parallel.map(0..1000000) {|elem| elem + 10}

ก็ได้

ผมอยากจะเขียน client ไปเรียก API แบบพร้อมกัน เพราะประมาณว่าเขียนแบบธรรมดาช้าใช้ CPU ได้แค่ 23% ของ 1 core ก็เลยจัดไปประมาณนี้ (ตัดรายละเอียดออกแล้ว)


Parallel.each(id_orgs, in_processes: 4) do |id|
   metadata = get_metadata(api_key, id)
   metadata = do_sth(metadata)
   send_metadata(api_key, metadata)
end

ผลคือแค่ใช้ 2 processor นี่ server ก็ไม่ไหว เพิ่มเป็น 16 นี่ถึงกับเว็บหลักเดี้ยงไปเลย T_T