veer66

veer66

ใน Python ตาม code ข้างล่าง append เป็น method ของ object a แต่พอมานับจำนวนสมาชิกใช้ len(a) ซึ่ง len เป็น built-in function แทน ถ้าอยากให้มีรูปแบบเดียวกันเปลี่ยนจาก len(a) ไปเป็น a.__len__() ก็ได้ แต่ก็จะมี ____ เกินมาอีก ใน Python เองก็คงจะมีหลักเกณฑ์ว่าเมื่อไหร่อะไรจะเป็น function อะไรจะเป็น method ซึ่งก็อาจจะต้องเรียนรู้หรือจำไว้

a = [1, 2, 3, 4, 5]
a.append(10)
print(len(a))

ใน JavaScript ก็มีรูปแบบที่ต่างกันคือ push เป็น method แต่ length เป็น property สังเกตได้จากว่าตอนเรียกไม่มีวงเล็บ ใน JavaScript ก็คงมีหลักการว่าเมื่อไหร่อะไรจะเป็น method หรือเป็น property ซึ่งก็ต้องเรียนรู้หรือจำไว้

a = [1, 2, 3, 4, 5]
a.push(10)
console.log(a.length)

ใน Ruby ทีนี้เหมือนกันแล้วคือ push ก็เป็น method และ length ก็เป็น method

a = [1, 2, 3, 4, 5]
a.push(10)
p(a.length)

แต่ก็ยังมีประเด็นต่อไปอีกว่าทำไมใน Ruby เขียนแบบนี้

10 + 20 + 30 + 40 + 50

ไม่เป็น method และเรียกเหมือนอย่างอื่นบ้างล่ะ

10.+(20).+(30).+(40).+(50)

(Emacs) Lisp จะต่างกับอันอื่นหน่อย ตรงที่ push มันไปต่อหน้าแทนที่จะต่อหลัง

(setq a '(1 2 3 4 5))
(push 10 a)
(print (length a))
(+ 10 20 30 40 50)

เขียน Lisp นี่ทุกอย่างก็จะเรียงเหมือนกันหมดเลย ได้แก่ (push ...) (length ...) (+ ...) ไม่เดี๋ยวต่อหน้า len(...) หรือเดี๋ยวต่อหลังเป็น a.length หรือว่าเดี๋ยวมีหรือไม่มีวงเล็บ เช่น a.length กับ a.length

แต่ปัญหาที่มันจะเจอคือหลายท่านรับไม่ได้ที่วงเล็บไปอยู่ข้างหน้าชื่อ function ถึงกับว่ามันวงเล็บย้ายที่แล้วเขียนไม่ได้เลย

แต่ Julia ก็เขียนแบบนี้ได้ append! ก็เป็น length ก็เป็น function ทั้งคู่ แถมตอนบวกก็เขียนแบบ function ได้ด้วย

a = [1,2,3,4,5]
append!(a,10)
print(length(a))
+(10,20,30,40,50) # จะเขียนแบบ 10 + 20 + 30 + 40 + 50 ก็ได้

แต่ก็อาจจะคิดได้ว่าทำเวลาสร้าง array ไม่เป็น function บ้าง ก็อาจจะเปลี่ยน [1,2,3,4,5] เป็น push!([],1,2,3,4,5) แทน ทีนี้ก็จะเหลือแต่ = แล้วที่ดูไม่เหมือนอย่างอื่น ซึ่งอันนี้ผมก็นึกวิธีแก้ให้มันเหมือนกันไม่ออก

from google.colab import drive
drive.mount('/content/drive')
import os
os.listdir('/content/drive/My Drive')

โพสต์นี้คนอ่านที่เป็นเป้าหมายหลักก็คือผมเอง

อย่างแรกเลยสมมุติว่า ElasticSearch ที่รันอยู่แล้ว

สร้าง dir ที่จะเก็บ snapshot และ chown ให้เป็นของ user เดียวกับที่ daemon ของ elasicsearch รันอยู่

แก้ elasticsearch.yml ที่ path.repo ให้ชี้ไปที่ dir ที่สร้างไว้ในข้อ 1 เช่น [“/bak/b1”] ไรงี้

backup/snapshot

สร้าง repo

curl -X PUT "localhost:9200/_snapshot/my_backup?pretty" -H 'Content-Type: application/json' -d'
{
  "type": "fs",
  "settings": {
    "location": "/bak/b1"
  }
}
'

restore

สร้าง repo

curl -X PUT "localhost:9200/_snapshot/my_backup/snapshot_1?wait_for_completion=true&pretty"

ตอนนี้ก็คือจะเสร็จแล้ว สิ่งที่ผมทำคือ rsync /bak/b1 ไปเครื่องอื่นแล้วลอง restore เพื่อไม่ให้น่าเบื่อเครื่องอื่นที่ว่าก็จะใช้ docker ด้วย

พอ rsync มาแล้ว ก็ chown -R 1000:1000 $(pwd)/bak/b1

สร้าง elasticsearch.yml

cluster.name: "docker-cluster"
network.host: 0.0.0.0
path.repo: ["/usr/share/elasticsearch/bak"]

รัน ElasticSearch ผ่าน Docker

docker run \
       --name=es6 \
       --net=host \
       -e "discovery.type=single-node" \
       -v $(pwd)/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml \
       -v $(pwd)/bak/b1:/usr/share/elasticsearch/bak \
       docker.elastic.co/elasticsearch/elasticsearch:6.8.4

สร้าง repo

curl -X PUT "localhost:9200/_snapshot/my_backup?pretty" -H 'Content-Type: application/json' -d'
{
  "type": "fs",
  "settings": {
    "location": "/usr/share/elasticsearch/bak"
  }
}
'

สั่ง restore

curl -X PUT "localhost:9200/_snapshot/my_backup?pretty" -H 'Content-Type: application/json' -d'
{
  "type": "fs",
  "settings": {
    "location": "/usr/share/elasticsearch/bak"
  }
}
'

เสร็จแล้ว

คำสั่งต่าง ๆ ก็มาจาก guide ครับ

ผมทำตามตัวอย่างที่ https://tantivy-search.github.io/examples/basic_search.html แต่โมพวก field แล้วก็แก้ให้เป็นสองไฟล์แบบที่อยากใช้งาน

Cargo.toml

[package]
name = "v_search"
version = "0.1.0"
authors = ["Vee Satayamas <vee.sa@protonmail.com>"]
edition = "2018"

[dependencies]
tantivy = "0.10.2"

[[bin]]
name = "make_index"
path = "src/make_index.rs"

[[bin]]
name = "search"
path = "src/search.rs"

make_index.rs

ส่วนที่ผมงงแรก ๆ เลยคือ STORED อ่านมาบอกว่าสามารถอ่านข้อมูลคืนมาด้วย id เข้าใจว่าถ้า field ไหนไม่ STORED คือทำ index อย่างเดียวจริง ๆ แต่ข้อมูลต้นฉบับหาย แต่ผมก็ยังไม่ลองว่าถ้าไม่ใส่มันจะไม่ได้เชียวหรือ ?

use tantivy::doc;
use tantivy::schema::*;
use tantivy::Index;

fn main() -> tantivy::Result<()> {
    let index_path = "idx"; 
    let mut schema_builder = Schema::builder();
    schema_builder.add_text_field("textunit", TEXT | STORED);
    let schema = schema_builder.build();
    let textunit = schema.get_field("textunit").unwrap();
    let index = Index::create_in_dir(&index_path, schema.clone())?;
    let mut index_writer = index.writer(50_000_000)?;

    index_writer.add_document(doc!(
        textunit => "A",
    ));

    index_writer.commit()?;
    Ok(())
}

search.rs

use tantivy::collector::TopDocs;
use tantivy::query::QueryParser;
use tantivy::schema::*;
use tantivy::Index;
use tantivy::ReloadPolicy;

fn main() -> tantivy::Result<()> {
    let index_path = "idx"; 
    let mut schema_builder = Schema::builder();
    schema_builder.add_text_field("textunit", TEXT | STORED);
    let schema = schema_builder.build();
    let textunit = schema.get_field("textunit").unwrap();
    let index = Index::open_in_dir(index_path)?;
    let reader = index
        .reader_builder()
        .reload_policy(ReloadPolicy::OnCommit)
        .try_into()?;
    let searcher = reader.searcher();

    let query_parser = QueryParser::for_index(&index, vec![textunit]);
    let query = query_parser.parse_query("A")?;
    let top_docs = searcher.search(&query, &TopDocs::with_limit(10))?;
    for (_score, doc_address) in top_docs {
        let retrieved_doc = searcher.doc(doc_address)?;
        println!("{}", schema.to_json(&retrieved_doc));
    }
    Ok(())
}

อันนี้ผมก็คือทำ index ไว้ใน directory ชื่อ idx แล้ว แล้วอีกไฟล์ก็มา search แค่นี้เลย

code แค่นี้เลย

fn cnt_links(s: usize, e: usize, tu: &Textunit, en_sent: &RTok) -> usize {
    tu.links.iter()
        .filter(|link| link.target > s && link.target < e)
        .filter(|link| link.source >= en_sent.s && link.source < en_sent.e)
        .count()
}

พอเขียน test แบบยาว ก็เจอจุดพัง

#[test]                                                                                                                                                
    fn cnt_links_simple() {
        let tu = Textunit {
            bi_text: BiText { source: String::from(""),
                              target: String::from(""),
            },
            bi_rtoks: BiRToks {
                source: vec![RTok {s:100, e:101, text: String::from(" ")},
                             RTok {s:102, e:103, text: String::from(" ")},],
                target: vec![RTok {s:300, e:301, text: String::from(" ")},
                             RTok {s:302, e:303, text: String::from(" ")},],
            },
            links: vec![Link {source: 0, target: 1},
                        Link {source: 1, target: 0}],

        };
        let en_sent = RTok {s: 302, e:303, text: String::from(" ")};
        assert_eq!(cnt_links(0, 1, &tu, &en_sent), 1);
    }

ระบบ type ช่วยอะไรไม่ค่อยได้เพราะมันเป็น integer หมด ยกเว้นจะใช้ type แบบที่มีหน่วยได้ แต่แบบนั้นก็ไม่รู้ว่าโปรแกรมจะยาวกว่านี้อีกเปล่า

รู้สึกว่าปัญหาของโปรแกรมนี้หลัก ๆ เลยคือ test มันยาวเกิน

มันเป็น API อยู่ที่ https://cloud.google.com/speech-to-text/ มีวิธีใช้พร้อม ผมเปลี่ยน config นิดหน่อย ตามนี้ครับ

เขามีไฟล์ PDFให้อ่านด้วยนะครับ

แต่แค่อยากลองว่า Google มันจะฟังออกแค่ไหน

config = { encoding:          :FLAC,
           sample_rate_hertz: 16_000,
           language_code:     "th-TH" }

ไฟล์เสียง

เพื่อใช้ในการวิจัยและไม่ได้มีการหาผลประโยชน์อะไรเลยนะครับ ตามนี้ https://file.veer66.rocks/nlp/sample1min7.flac

วิธีเตรียมไฟล์เสียงเพื่องานวิจัยและไม่ได้มีการหาผลประโยชน์

อันนี้เอาไฟล์เตรียมไฟล์ video ก่อนเพื่องานวิจัยและไม่ได้มีการหาผลประโยชน์

ffmpeg -i http://live.parliament.go.th/livestream/encoder3/playlist.m3u8 -c copy -bsf:a aac_adtstoasc output.mp4

แล้วก็แปลงเป็นเสียงเพื่องานวิจัยและไม่ได้มีการหาผลประโยชน์

ffmpeg -i output.mp4 sample.mp3

หลังจากนั้นผมใช้ Audacity แก้เสียง sample.mp3 ให้เป็น mono 16-bit แล้ว save เป็น .flac ครับ เพื่องานวิจัยและไม่ได้มีการหาผลประโยชน์อีกเช่นเคย

ผลออกมาได้งี้

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

I guess many people wrote this kind of scripts already. But sometimes searching is more complicated than just writing a new one. 😅 If you know a keyword, please tell me.

This script should work with other commands. I use target/release/main because I want to test my program built from Rust's Cargo. I suppose other commands can replace target/release/main.

#!/bin/sh                                                                                                                                                                   
                                                                                                                                                                            
CMD=target/release/main                                                                                                                                                     
                                                                                                                                                                            
$CMD > log 2>&1 &                                                                                                                                                           
export PID=$!                                                                                                                                                               
echo "Process $PID is running ..."                                                                                                                                          
                                                                                                                                                                            
trap "echo kill $PID; kill $PID; exit" INT QUIT                                                                                                                             
                                                                                                                                                                            
rm -f mem.tsv                                                                                                                                                               
while true                                                                                                                                                                  
do                                                                                                                                                                          
    tm=`date '+%Y-%m-%dT%H:%M:%S'`                                                                                                                                          
    rss=`ps -u | awk -v PID=$PID '{ if ($2 == PID) { print $6; } }'`                                                                                                        
    echo "$tm\t$rss" >> mem.tsv                                                                                                                                             
    sleep 10                                                                                                                                                                
done                                                                                                                                                                        

It logs memory usage in mem.tsv, so I should be able to plot a graph by LibreOffice Calc easily.

When the producer sends messages too frequently, and the producer takes too much time to process each message. I wrote a code for

I want to discard aging messages and process only the latest one. So I wrote a code for repeating receive messages until the channel is empty for a short duration. However, I found my code was too complicated. So I use shared memory with mutex-lock for inter-thread communication instead of a channel.

use std::sync::{Mutex, Arc};
use std::{thread, time};
use std::collections::HashMap;

fn main() {
    let r_shared_data = Arc::new(Mutex::new(None));
    let w_shared_data = r_shared_data.clone();
    
    thread::spawn(move || {
        let tx_delay_time = time::Duration::from_millis(100);
        loop {
            for i in 1..1000 {
                let mut h = HashMap::new();
                h.insert("X", i);
                let mut w = w_shared_data.lock().unwrap();
                *w = Some(h);
                if i % 10 == 0 {
                   thread::sleep(tx_delay_time);
                }                
            }
        }
    });

    let task_proc_time = time::Duration::from_millis(500);
    loop {
         let r = r_shared_data.lock().unwrap();
        dbg!(r);
        // long task
        thread::sleep(task_proc_time);
    }
}

เดิมโพสต์เมื่อ 2015-06-25

ภาษาใหม่ ๆ เดี๋ยวนี้ ทั้ง Rust, Swift, F#, Scala หรือภาษากลางเก่ากลางใหม่แบบ C#, Java 8, Python, Ruby, JavaScript มักจะมีกลิ่นอายของ Lisp/Scheme ติดมาด้วยเสมอเช่นการ ใช้ closure กับ map, foldl, foldr, filter เป็นต้น

เนื่องจากว่าเขียนได้ทั้งแนว Fortran และแนว Lisp เวลาคนเขียนภาษาใหม่ ๆ เช่น Python บางทีก็จะเถียงกัน (ผมเห็นจากใน blog ของคนอื่น) ว่าจะใช้ map filter reduce ทำไม อ่านยากจะแย่ จะเขียนสั้น ๆ ทำไม ต้องใช้ for-loop แทนสิ บางคนก็บอกว่า map filter reduce มันก็ดีอยู่แล้ว เขียน for-loop มันอ่านง่ายตรงไหน

ส่วนภาษา Go เป็นภาษาใหม่ที่เน้น imperative (แนว Fortran) ล้วน ๆ และออกแนว minimalism คำสั่งที่มีมาให้ก็เป็นไปแต่แบบพอเพียง บางอย่างที่เขาคิดว่าไม่จำเป็นเขาก็ไม่มีให้ ทำให้แน่นอนไม่มี map, fold, foldr, filter แถมมาให้ ถ้าอยากได้ก็ต้องทำเองในระดับ library

แน่นอนว่าในระดับ Library นี้ compiler เข้ามาช่วย optimize ให้ได้ยาก พอใช้ไปก็จะทำให้โปรแกรมช้าได้

ด้วย 2 เหตุนี้คือขัดวิถีโกและช้า ทำให้โอกาสที่ใคร ๆ จะเขียนแนว Lisp/Scheme ใน Go มีน้อย

ดังนั้นใครหรือทีมไหนที่ไม่ชอบเขียนโปรแกรมที่มีกลิ่นอายของ Lisp/Scheme ในยุคนี้ภาษา Go เหมาะอย่างยิ่ง

Sometimes a consumer takes a long time to finish a task, and producers keep sending data. This situation can cause a memory leak. Keep processing out-dated data makes no sense in some applications.

So I write a program to keep receiving data within 10ms time frame, and discard old data.

use std::sync::mpsc::channel;
use std::{thread, time};

fn main() {
    let (tx, rx) = channel();
    thread::spawn(move || {
        let tx_delay_time = time::Duration::from_millis(100);
        loop {
            for i in 1..1000 {
                tx.send(i).expect("Cannot send");
                if i % 10 == 0 {
                    thread::sleep(tx_delay_time);
                }
            }
        }
    });

    let timeout = time::Duration::from_millis(10);
    loop {
        println!("LOOP");
        let mut i = None;
        loop {
            match rx.recv_timeout(timeout) {
                Ok(j) => {
                    i = Some(j);
                },
                Err(_) => {
                    if i.is_some() {
                        break;
                    }
                }
            }
        }
        // long task ...
        println!("{:?}", i);
        i = None;
    }
}

I feel that the program look too complicated. Perhaps I shouldn't a channel at all in this case. 🤣