ทดลองใส่โปรแกรม
File.open("toto") do |file|
file.each_line |line|
line.chomp!
end
end
veer66
File.open("toto") do |file|
file.each_line |line|
line.chomp!
end
end
พอทดสอบกับไฟล์ 11MB ใช้ pypy3 ทำให้ wordcutpy เร็วขึ้นเกิน 2 เท่า! คือใช้เวลาจาก 16 วินาที เหลือไม่ถึง 8 วินาที
(base) [vee@mint310 wiki]$ python3 wordcutpy.py
16598
(base) [vee@mint310 wiki]$ sudo docker run -it --rm --name my-running-script -v "$PWD":/usr/src/myapp -w /usr/src/myapp pypy:3 pypy3 wordcutpy.py
7833
(base) [vee@mint310 wiki]$ python3 wordcutpy.py
16093
(base) [vee@mint310 wiki]$ sudo docker run -it --rm --name my-running-script -v "$PWD":/usr/src/myapp -w /usr/src/myapp pypy:3 pypy3 wordcutpy.py
7821
(base) [vee@mint310 wiki]$ python3 wordcutpy.py
16272
(base) [vee@mint310 wiki]$ sudo docker run -it --rm --name my-running-script -v "$PWD":/usr/src/myapp -w /usr/src/myapp pypy:3 pypy3 wordcutpy.py
7810
# wordcutpy.py
# การใช้ wordcutpy ที่ถูกต้องคือ copy & paste เลย ไม่ต้องใช้ pip 😅
# แล้วก็ copy bigthai.txt มาไว้ folder เดียวกัน
import sys
import re
class PrefixTree(object):
def __init__(self, members_with_payload):
self.tab = {}
if members_with_payload is None:
return
sorted_members_with_payload = sorted(members_with_payload,
key=lambda i: i[0])
for i in range(len(sorted_members_with_payload)):
members, payload = sorted_members_with_payload[i]
row_no = 0
for j in range(len(members)):
is_terminal = len(members) == j + 1
member = members[j]
key = (row_no, j, member)
if key in self.tab:
row_no = self.tab[key][0]
else:
val = (i, is_terminal, payload if is_terminal else None)
self.tab[key] = val
row_no = i
def lookup(self, i, offset, member):
key = (i, offset, member)
if key not in self.tab:
return None
return self.tab[key]
UNK = 1
DICT = 2
INIT = 3
LATIN = 4
PUNC = 5
def is_better(link0, link1):
if link0 is None:
return True
if link1["unk"] < link0["unk"]:
return True
if link1["w"] < link0["w"]:
return True
return False
def build_path(dix, s):
left_boundary = 0
dict_acc_list = []
path = [{"p":None, "w": 0, "unk": 0, "type": INIT}]
latin_s = None
latin_e = None
punc_s = None
punc_e = None
for i, ch in enumerate(s):
dict_acc_list.append({"s":i, "p":0, "final":False})
# Update dict acceptors
_dict_acc_list = dict_acc_list
dict_acc_list = []
for acc in _dict_acc_list:
offset = i - acc["s"]
child = dix.lookup(acc["p"], offset, ch)
if child is not None:
child_p, is_final, payload = child
dict_acc_list.append({"s":acc["s"], "p": child_p,
"final":is_final})
# latin words
if latin_s is None:
if re.match(u"[A-Za-z]", ch):
latin_s = i
if latin_s is not None:
if re.match(u"[A-Za-z]", ch):
if i + 1 == len(s) or re.match(u"[A-Za-z]", s[i + 1]):
latin_e = i
else:
latin_s = None
latin_e = None
# puncuation
if punc_s is None:
if ch == " ":
punc_s = i
if punc_s is not None:
if ch == " ":
if len(s) == i + 1 or s[i + 1] != " ":
punc_e = i
else:
punc_s = None
punc_e = None
# select link
link = None
# links from wordlist
for acc in dict_acc_list:
if acc["final"]:
p_link = path[acc["s"]]
_link = {"p": acc["s"],
"w": p_link["w"] + 1,
"unk": p_link["unk"],
"type": DICT}
if is_better(link, _link):
link = _link
# link from latin word
if latin_s is not None and latin_e is not None:
p_link = path[latin_s]
_link = {"p": latin_s,
"w": p_link["w"] + 1,
"unk": p_link["unk"],
"type": LATIN}
if is_better(link, _link):
link = _link
# link from puncuation
if punc_s is not None and punc_e is not None:
p_link = path[punc_s]
_link = {"p": punc_s,
"w": p_link["w"] + 1,
"unk": p_link["unk"],
"type": PUNC}
if is_better(link, _link):
link = _link
# fallback
if link is None:
p_link = path[left_boundary]
link = {"p": left_boundary,
"w": p_link["w"] + 1,
"unk": p_link["unk"] + 1,
"type": UNK}
path.append(link)
if link["type"] != UNK:
left_boundary = i
return path
def path_to_tokens(txt, path):
if len(path) < 2:
return None
e = len(path) - 1
toks = []
while True:
link = path[e]
s = link["p"]
if s is None:
break
toks.append(txt[s:e])
e = s
toks.reverse()
return toks
def tokenize(dix, txt):
if txt is None or txt == "":
return []
path = build_path(dix, txt)
return path_to_tokens(txt, path)
class Wordcut(object):
def __init__(self, wordlist):
self.dix = PrefixTree([(word, None) for word in wordlist])
@classmethod
def bigthai(cls):
import os
"Initialize from bigthai"
fileDir = os.path.dirname(__file__)
filename = os.path.join(fileDir, 'bigthai.txt')
with open(filename) as dict_file:
word_list = list(set([w.rstrip() for w in dict_file.readlines()]))
word_list.sort()
return cls(word_list)
def tokenize(self, s):
return tokenize(self.dix, s)
wordcut = Wordcut.bigthai()
import time
t1 = int(round(time.time() * 1000))
with open("wiki_plain_100k.txt") as fi:
with open("wiki.cut", "w") as fo:
for line in fi:
line = line.strip()
print(" ".join(wordcut.tokenize(line)), file=fo)
t2 = int(round(time.time() * 1000))
print(t2-t1)
# LICENSE: LGPLv3
(เริ่มเขียนเมื่อ 2018-11-25)
ข้อเสียของเครือข่ายสังคมออนไลน์คือสมาชิกมีอำนาจต่อรองน้อย ทำได้อย่างมากเพียงแค่ย้ายเครือข่าย แต่พอย้ายแล้วก็คุยกับคนที่อยู่อีกเครือข่ายไม่ได้ เวลาลดน้ำหนักอยากดูวอลเลย์บอลเพื่อความบันเทิงก็อาจจะเลี่ยงไม่ได้ที่จะต้องดูโฆษณาไก่ทอดเสียก่อน
หรือว่าบางเจ้าบอกว่าไม่ให้พื้นที่ 1TB แล้ววันหนึ่งจะเก็บตังรายปีขึ้นมา เพียงว่าเราจะย้ายข้อมูล เช่น รูปภาพที่เราถ่ายเอง กล้องถ่ายภาพทางเว็บก็ไม่ได้แจกมา เอาไปไว้ที่อื่นที่อาจจะดีกว่า หรือราคาถูกกว่า ก็เป็นเรื่องที่ยากลำบากแล้วในกรณีที่มีข้อมูลปริมาณมาก
อีกประเด็นหนึ่งคือเครือข่ายสังคมออนไลน์แบบผูกขาดได้ผูกขาดทางศีลธรรมด้วยว่าอะไรดีไม่ดี อะไรโพสต์ได้ อะไรโดนลบ มากไปกว่านั้นผู้ควบคุมก็มักจะเป็นโปรแกรม เพราะว่าเครือข่ายขนาดใหญ่ก็ใช้คนทำไม่ไหว
เครือข่ายสังคมออนไล์ที่ผู้ใช้ย้ายออกลำบากตามที่ว่ามาก็เป็นโอกาสให้เอาสมาชิกของสังคมไปขายได้ด้วย อาจจะขายพ่วงกับกิจการ หรือว่าข่ายให้ผู้ที่อยากโฆษณาอีกทีก็ทำได้
ในระบบที่เป็นที่นิยมก่อนหน้านี้ เช่น E-mail เมื่อเปลี่ยนผู้ให้บริการก็ยังติดต่อกับญาติมิตรที่ใช้บริการ E-mail เจ้าอื่นได้ หรือแม้แต่กระทั่งโทรศัพท์เปลี่ยนผู้บริการแล้วก็ยังโทรหาคนอื่นได้เหมือนเดิม
Fediverse เกิดจากคำว่า Federation และ Universe รวมกันเป็นเครือข่ายสังคมออนไลน์ รวมถึง blog ที่เครื่องแม่ข่ายต่าง ๆ ทำงานร่วมกันได้ถึงแม้ว่าจะไม่ได้มีเจ้าของเดียวกัน หรือกระทั่งไม่ได้ใช้โปรแกรมเดียวกัน เช่น โปรแกรมสำหรับเขียน micro blog สามารถแสดงความคิดเห็นต่อวิดีโอบนเว็บสำหรับเผยแพร่วิดีโอได้โดยตรง
หลังจากที่โพสต์วิดีโอบน peertube.social ซึ่งเว็บสำหรับโพสต์วิดีโอใน Fediverse เราสามารถค้นหาด้วย URL ของ PeerTube จาก mstdn.io ซึ่งเป็นเว็บสำหรับโพสต์ micro blog ก็จะเห็นว่าวิดีโอใน PeerTube เป็น blog post หนึ่งจาก mstdn.io; สามารถ reply ได้; พอ reply เสร็จแล้วก็จะขึ้นไปเป็นความคิดของวิดีโอนั้นเลย
นอกจากจะแสดงความคิดเห็นข้ามกันได้แล้ว เรื่องทั่วไปอย่างดู video ของ PeerTube ใน Mastodon ก็ทำได้อยู่แล้ว หรือจะ follow/subscribe ก็ทำได้เหมือนกัน
ซึ่งลักษณะที่ว่าทำให้ Fediverse มีเครื่อแม่ขายที่ไม่ได้มีเจ้าของเดียวกันเยอะแยะไปหมด จากเว็บนี้ก็บอกว่ามี 2460 node แล้ว
ผมเลือก node ที่ผมคิดว่าสำคัญมาตามนี้
โปรแกรม Mastodon, PeerTube, Friendica, Pixelfed ล้วนเป็นซอฟต์แวร์เสรีทั้งสิ้น ซึ่งหมายความว่าเราจะเปิด node ของเราเองก็ได้ จะแก้ไขดัดแปลงแจกจ่ายก็ได้ แต่ละโปรแกรมก็ใช้ภาษาโปรแกรมต่างกันออกไป Mastodon ใช้ Ruby, PeerTube ใช้ TypeScript ส่วน Friendica และ Pixelfed ใช้ PHP
Fediverse แต่ละ node คุยกันได้เพราะว่ามีมาตรฐานกลาง ซึ่งหลัก ๆ ก็คือ ActivityPub ที่สร้างมาบน Acivity Stream และ JSON-LD อีกที
JSON-LD หลักการคล้าย ๆ RDF คือพยายามจะทำให้ข้อมูลจากแหล่งต่าง ๆ เชื่อมโยงกันได้ แต่ว่าใช้ JSON parser ได้เลยเพราะมันเป็น JSON เช่น เปิด json มาอาจจะ field ชื่อ actor ก็อาจจะหมายถึง “นักแสดงชาย” หรือ “ผู้กระทำ” ก็ได้ แต่ถ้าเขียนเลยว่า “https://www.w3.org/ns/activitystreams/actor” อันนี้ก็จะหมายถึงผู้กระทำแน่ ไม่จำเป็นต้องเป็นนักแสดง แต่จะเขียนชื่อ field ด้วย url หรือในเอกสารจะเรียกรวม ๆ ว่า IRI มันก็จะเยิ่นเย้อเกินไป ก็เลยมี field หนึ่งขึ้นมาชื่อว่า @context เราก็เรียน “@context”: “https://www.w3.org/ns/activitystreams” ไว้เสียแต่ต้น ต่อไปในเอกสารก็ใช้ actor ได้เลยเพราะทราบแล้วว่าบริบทคือ “https://www.w3.org/ns/activitystreams” สามารถอ่านเพิ่มเติมได้จาก JSON-LD Spec
ส่วน Activity Stream เป็นมาตรฐานที่ระบุเลยว่าจะเขียนว่าใคร (actor) ทำอะไร (verb) อะไรถูกกระทำ (object) จะต้องเขียนออกมาหน้าตาอย่างไร หน้าตาก็จะออกมาเป็น JSON-LD ตามที่มาตรฐานกำหนดไว้ ที่ผมเห็นใช้งานตอนนี้ก็คือใช้ในเครือข่ายสังคมออนไลน์ทั้งหมด อ่านเพิ่มเติมได้จาก Activity Streams Spec
จาก Activity Stream ก็มีมาตรฐานเกี่ยวเนื่องที่ผมรู้สึกว่าน่าสนใจคือ Activity Vocabulary ซึ่งระบุกระทั่งว่ากริยาที่กระทำใน Activity Stream มีอะไรหน้าตาเป็นอย่างไร เช่น like dislike follow invite ignore ต้องมีหน้าตา มีข้อมูลอย่างไรบ้าง อ่านเพิ่มเติมได้จาก Vocabulary spec
นอกจากประเด็นว่าส่งว่าใคร ทำอะไร อะไรเป็นผู้กระทำแล้ว ก็ยังมีประเด็นว่าต้องส่ง activity stream ไปที่ไหน ในส่วนนี้ก็เป็นมาตรฐาน ActivityPub ซึ่งกำหนดทั้งการส่งจาก client กับ server และ server กับ server เอง เพราะว่า Fediverse ต้องมีการสือสารกันระหว่าง server คนละเจ้าด้วย โดยมีการกำหนดเป็น inbox outbox ของแต่ละ actor ขึ้นมาว่าจะอยู่ใน URL อะไร ฯลฯ ซึ่งอ่านรายละเอียดได้จาก ActivityPub Spec
ใน Fediverse ไม่มีการเอาการติดต่อญาติมิตรของใคร ๆ มาเป็นตัวประกันของแต่ละเจ้าอีกต่อไป ทำให้ลดการผูกขาดลด เมื่อมีอำนาจต่อรองมากขึ้นสมาชิกก็ถูกนำไปขายยากขึ้น แต่ว่า node สามารถนโยบายของตัวเองทำให้ไม่มีการผูกขาดทางศีลธรรม เรื่องโฆษณาใน Fediverse ตอนนี้ยังไม่เห็นครับ ส่วนหนึ่งคือ admin ของแต่ละ node ออกตังเองเพราะแต่ละ node ไม่ใช้เครื่องแรงมากมายพอจะออกให้กันได้ และอีกส่วนหนึ่งก็มีการบริกาคช่วยกัน; ผมเห็น write.as มีแบบเสียตังด้วย แต่ยังต้องศึกษาเพิ่มเติมอีก
แต่ก็มีข้อจำกัดอยู่ว่าย้ายค่ายแล้วต้องเปลี่ยน ID
อนาคตอาจจะมีการใช้ ActivityPub กับบริการออนไลน์อื่นอีก ไม่ว่าจะเป็นซอฟต์แวร์เสรีหรือไม่ก็ตาม หรือก้าวหน้าจนไปถึงแบบ peer-to-peer