<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: armariya</title>
    <description>The latest articles on DEV Community by armariya (@armariya).</description>
    <link>https://dev.to/armariya</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F46453%2F53a62d2a-f5f7-4a5b-818c-ff11e3d0aee1.png</url>
      <title>DEV Community: armariya</title>
      <link>https://dev.to/armariya</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/armariya"/>
    <language>en</language>
    <item>
      <title>Long time no see!</title>
      <dc:creator>armariya</dc:creator>
      <pubDate>Sun, 03 Sep 2023 13:01:32 +0000</pubDate>
      <link>https://dev.to/armariya/long-time-no-see-olp</link>
      <guid>https://dev.to/armariya/long-time-no-see-olp</guid>
      <description>&lt;p&gt;Ayooo long time no see everyone! I'm back! I hope everyone still have a good life.&lt;/p&gt;

&lt;p&gt;Have a nice day.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>What the f**k is Program Derived Address (PDA) in Solana really?</title>
      <dc:creator>armariya</dc:creator>
      <pubDate>Mon, 07 Feb 2022 16:07:30 +0000</pubDate>
      <link>https://dev.to/armariya/what-the-fk-is-program-derived-address-pda-in-solana-really-o0o</link>
      <guid>https://dev.to/armariya/what-the-fk-is-program-derived-address-pda-in-solana-really-o0o</guid>
      <description>&lt;p&gt;ช่วงนี้ได้หันมาลองเขียน contract บน Solana ดูครับ นอกจากจะงงชิบหายกับ Accounts (ใครมันตั้งชื่อ จะตามไปด่า)​ ยังมางงต่อกับ Program Derived Address อีก ว่ามันอะไรวะเนี่ยยย ต้องใช้ตอนไหน ใช้ยังไงฟะะ อันนี้ก็เป็นจดบันทึกสำหรับตัวเอง เผื่อวันหน้างงจะกลับมาดูใหม่&lt;/p&gt;

&lt;h3&gt;
  
  
  So what is PDA?
&lt;/h3&gt;

&lt;p&gt;เดี๋ยวเอาจาก Solana Document มาแปะก่อน&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;An account whose owner is a program and thus is not controlled by a private key like other accounts.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;ถ้าอ่านตามตรงเลยก็คือ Account ที่เจ้าของเป็นโปรแกรมแล้วก็ไม่ได้ถูกควบคุมด้วย Private Key เหมือน Account อื่น ๆ&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jSn5rJq---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://cdn-images-1.medium.com/max/480/1%2Ao4lpspg8VZc7O2Qyu9_JeQ.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jSn5rJq---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://cdn-images-1.medium.com/max/480/1%2Ao4lpspg8VZc7O2Qyu9_JeQ.gif" alt="" width="480" height="260"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;ตอนอ่านครั้งแรกนี่คืองงไปเลยเหมือนกัน ว่ามันทำได้ไง ใช้อย่างไรวะเนี่ย ถ้าให้แปลให้มันเหมือนจะง่ายขึ้นอะนะ ก็คือ Public Key ที่ไม่มี Private Key แต่ว่าสามารถ *magically* sign ด้วย Program ได้ ช่วยไหม 555&lt;/p&gt;

&lt;h3&gt;
  
  
  วนกลับมาที่ Public Key, Private Key ใน Solana ก่อน
&lt;/h3&gt;

&lt;p&gt;KeyPair บน Solana มีขนาด 64 bytes โดย 32 bytes แรกจะเป็น Private Key (บางก็เรียก Secret Key) และ 32 bytes หลังจะเป็น Public Key&lt;/p&gt;

&lt;p&gt;โดย KeyPair พวกจะต้องอยู่บน Mathematic Curve โดย Solana ใช้ ed25519 curve โดย ณ จุดใดจุดหนึ่งถ้าอยู่บน Curve นี้จะมี KeyPair อยู่ตรงนั้นเสมอหรือคือมี (Private Key, Public Key) เสมอ โดยถ้าเราย้อนกลับไปดูคำที่เขานิยามของ PDA ใหม่&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;An account whose owner is a program and thus is not controlled by a private key like other accounts.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;จะพบว่า PDA ก็คือ Public Key ที่ไม่มี Private Key ซึ่งก็คือจุดใด ๆ ที่ไม่ได้อยู่บน ed25519 curve นั่นแหละ เพราะจุดที่ไม่ได้ตกลงบน Curve จะไม่มี Private Key ที่สอดคล้องกับ Public Key มีแต่เพียง Public Key เท่านั้น นั่นแปลได้ว่าจะไม่มีใครสามารถ sign transaction ที่มันเข้ากับ Public Key นี้ได้ ยกเว้นแต่พลังแห่ง Solana Program เท่านั้น *MAGIC* 🧙‍♂️&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8DCegh-E--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://cdn-images-1.medium.com/max/480/1%2AHvyGRugQwfERyOTk9btWDw.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8DCegh-E--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://cdn-images-1.medium.com/max/480/1%2AHvyGRugQwfERyOTk9btWDw.gif" alt="" width="480" height="358"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;โดยเราสามารถหา PDA ได้จากฟังก์ชัน&lt;/p&gt;

&lt;p&gt;let (pda, pda_bump) = PublicKey::find_program_address([seeds], program_id)&lt;/p&gt;

&lt;p&gt;โดยฟังก์ชันนี้จะทำ mapping จากจุด Program Id ที่อยู่บน Curve ไปยังอีกจุดหนึ่งซึ่งจะเป็นจุดที่อยู่หรือไม่อยู่บน Curve ก็ได้ และเพื่อให้มันการันตีว่าจะอยู่นอก Curve แน่ ๆ เลยมีการบวก Bump เข้าไปเพื่อให้มันหลุดออกจาก Curve แน่นอนหรือแปลว่าไม่มี Private Key ที่สอดคล้องกับ PDA อย่างแน่นอน นี่มันช่วยจริงหรือไม่ รู้สึกไม่มั่นใจเอาเสียเลย 5555&lt;/p&gt;

&lt;h3&gt;
  
  
  แล้ว PDA เอาไว้ทำอะไรกันแน่ฟะ
&lt;/h3&gt;

&lt;p&gt;จริง ๆ ถ้าเราแอบแปลคำว่า Program Derived Address จะแปลว่า Address ที่จากมาจาก Program นั่นแล หมายความว่าถ้าเรามี Program Id เราจะสร้าง PDA กี่อันก็ได้ ด้วยการใส่ Seed ต่าง ๆ กันไปตามต้องการ&lt;/p&gt;

&lt;p&gt;ประโยชน์ของมันจริง ๆ ก็เอาไว้ใช้ตอนที่ต้องการจะให้โปรแกรมถือ Accounts อะไรบางอย่าง ที่ควรจะต้อง Sign ได้ด้วยเช่น ถ้าเราต้องการให้ Program เราถือ TokenAccount ที่เอาไว้เก็บเหรียญ​ สมมติถ้าเราให้ Program เนี่ยถือ TokenAccount ตรง ๆ เลย เราจะไม่มีหนทางให้ตัว Program ทำการ sign transaction ได้ หรือแปลว่าเราจะไม่มีทางที่จะ Transfer Token ออกจาก Accounts นั้นได้เลย ถือไปตลอดกาล (อาจจะมีก็ได้แหละ แต่เท่าที่รู้ตอนนี้ยังไม่มี มีก็บอกด้วยนะครับ ขอบคุณครับ) แทนที่จะเป็นเช่นนั้นก็เลยให้ PDA เนี่ยเป็นคนถือ TokenAccounts แทน แล้วเวลาจะ Transfer ก็ทำการ sign transaction โดยให้ Solana มัน *magically* sign ให้ ตราบใดที่เรามี Seed, Bump เราก็จะให้ Program sign transaction ได้เสมอนั่นเอง&lt;/p&gt;

&lt;p&gt;เอาจริง ๆ เขียนมาแล้วรู้สึกว่ามันไม่น่าจะมีใครอ่านรู้เรื่อง แต่ก็นั่นแหละ ตอนนี้เอาตัวเองอ่านรู้เรื่องก่อนแล้วก่อน กราบขออภัยถ้าใครเข้ามาอ่านแล้วมันไม่เข้าใจอะนะ&lt;/p&gt;

&lt;h3&gt;
  
  
  แหล่งอ้างอิง
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Solana Bootcamp Chicago — Day 2 — Lecture: Program derived addresses and cross program invocation&lt;/strong&gt;  — &lt;a href="https://www.youtube.com/watch?v=iMWaQRyjpl4&amp;amp;t=2917s"&gt;https://www.youtube.com/watch?v=iMWaQRyjpl4&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.solana.com/terminology#program-derived-account-pda"&gt;https://docs.solana.com/terminology#program-derived-account-pda&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>solanablockchain</category>
    </item>
    <item>
      <title>Radicle หนทางใหม่ของ code collaboration</title>
      <dc:creator>armariya</dc:creator>
      <pubDate>Fri, 04 Dec 2020 16:33:33 +0000</pubDate>
      <link>https://dev.to/armariya/radicle-code-collaboration-2bom</link>
      <guid>https://dev.to/armariya/radicle-code-collaboration-2bom</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;"FREE YOUR CODE" &lt;br&gt;
นี่คือประโยคแรกที่เราจะเจอ หลังจากเข้าไปที่เว็บไซต์  &lt;a href="https://radicle.xyz"&gt;radicle.xyz&lt;/a&gt; &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;เมื่อวันที่ 30 พฤศจิกายน 2020 radicle ได้ทำการปล่อย first beta release แรก หลังจากที่ซุ่มทำมานาน ซึ่งผมคิดว่านี่มันวันแห่งประวัติศาสตร์ชัด ๆ ! &lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1333403629961797635-269" src="https://platform.twitter.com/embed/Tweet.html?id=1333403629961797635"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1333403629961797635-269');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1333403629961797635&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;h3&gt;
  
  
  Radicle คืออะไร?
&lt;/h3&gt;

&lt;p&gt;Radicle เป็น decentralized application สำหรับการแชร์โค้ดนั่นเอง ถ้าใครนึกไม่ออกมันคือเหมือนกับ Github, Gitlab ที่เป็น decentralized นั่นเอง สิ่งที่ radicle ต้องการก็คือต้องการให้ทุกคนสามารถแชร์โค้ดได้ โดยไม่ต้องพึ่งตัวกลางที่เป็น third-party และจะไม่มีใครมาสามารถปิด repository ของเราลงได้ ถ้าเราไม่ยอม (จริง ๆ น่าจะเป็นการแซะถึงเหตุการณ์ที่ youtube-dl โดนปิดบน Github เมื่อไม่นานที่ผ่านมา ถึงทาง Github จะเอากลับมาแล้วก็เถอะ &lt;a href="https://www.theverge.com/2020/11/17/21571473/github-youtube-dl-downloader-riaa-copyright-1201-takedown-reinstated#:~:text=The%20Recording%20Industry%20Association%20of,the%20Digital%20Millennium%20Copyright%20Act."&gt;อ่านเพิ่มเติม&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;โดยตัว Radicle ถูกสร้างขึ้นมาโดยใช้ git เหมือนเดิม ทำให้คนที่เคยใช้ git มาแล้วไม่จำเป็นต้องเปลี่ยน workflow อะไร ยังคง commit, push, merge, branch ได้เหมือนเดิม&lt;/p&gt;

&lt;h3&gt;
  
  
  แล้ว Radicle แชร์โค้ดแบบ peer-to-peer ได้ยังไง?
&lt;/h3&gt;

&lt;p&gt;แทนที่จะเอาโค้ดเนี่ยไปเก็บบน central server เหมือนปกติทั่วไป Radicle ใช้ Radicle Link เป็น protocol ในการทำ peer discovery โดยเครื่องแต่ละเครื่องจะทำหน้าที่เก็บและแชร์โค้ดที่ตัวเองสนใจไว้นั่นเอง&lt;/p&gt;

&lt;p&gt;แล้วคนอาจจะคิดว่าอ้าวแล้วแบบนี้โปรเจ็คมันจะไม่หายเรอะ ถ้าพลาดแล้วไม่มีใครเก็บไว้สักคน คำตอบก็คือมันก็ต้องหายนั่นแหละ ถ้าเราเป็นบริษัทล่ะ? อยากจะให้แบบมีที่เก็บโค้ดตัวกลางของบริษัท ที่คนในบริษัทสามารถเห็นโปรเจ็คทั้งหมดได้ทำยังไง เราสามารถตั้ง seed node เองได้ โดย seed node สามารถเซตให้เห็นเฉพาะ peer ได้ด้วย&lt;/p&gt;

&lt;p&gt;ถ้าใครอยากลอง วันนี้ก็สามารถเข้าไปที่ &lt;a href="https://radicle.xyz"&gt;radicle.xyz&lt;/a&gt; แล้วโหลดมาลองกันได้เลย&lt;/p&gt;

&lt;p&gt;อ้างอิง&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Radicle: &lt;a href="https://radicle.xyz"&gt;radicle.xyz&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;Radicle Link: &lt;a href="https://docs.radicle.xyz/docs/what-is-radicle.html"&gt;https://docs.radicle.xyz/docs/what-is-radicle.html&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>git</category>
      <category>radicle</category>
    </item>
    <item>
      <title>เหนื่อยไหมกับการ cd รัว ๆ แล้วไม่ถึงสักที</title>
      <dc:creator>armariya</dc:creator>
      <pubDate>Wed, 24 Jun 2020 15:29:37 +0000</pubDate>
      <link>https://dev.to/armariya/cd-5eci</link>
      <guid>https://dev.to/armariya/cd-5eci</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PYEljmku--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/400/1%2AIu_QsguGpWs-iSXD3mxIkQ.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PYEljmku--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/400/1%2AIu_QsguGpWs-iSXD3mxIkQ.jpeg" alt="" width="400" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;เหนื่อยไหมกับการพิมพ์ path โคตรยาวแล้วดันพิมพ์ผิดไปตัวนึงซะงั้น&lt;/p&gt;

&lt;p&gt;สวัสดีครับทุกคน กลับมาอีกครั้งนะครับ นาน ๆ มาที วันนี้นี่มี tool มานำเสนออีกแล้วนั่นเองงง&lt;/p&gt;

&lt;p&gt;ถ้าใครที่เคยใช้ terminal คงจะเคยเจอปัญหาแบบนี้ใช่ไหมครับ&lt;br&gt;&lt;br&gt;
$ cd Developer/unity/platformer&lt;br&gt;&lt;br&gt;
ยิ่งถ้าเราต้องเข้า Directory นี้บ่อย ๆ เนี่ยมันจะน่ารำคาญเอามาก ๆ แม้จะมีการกด Tab ช่วยมันก็ยังเหนื่อยอยู่ดี&lt;/p&gt;

&lt;p&gt;และสิ่งที่จะมาช่วยเราในคราวนี้ก็คืออออ “z” นั่นเอง&lt;/p&gt;

&lt;p&gt;สามารถเข้าไปดู z ได้ที่ &lt;a href="https://github.com/rupa/z"&gt;https://github.com/rupa/z&lt;/a&gt; ถ้าเกิดว่าคุณใช้แมคล่ะก็สามารถที่จะ brew install z ได้เลย&lt;/p&gt;

&lt;p&gt;โดยความสามารถของ z ก็คือ z จะตามหา path ที่เราเขาบ่อยสุด แล้วจะกระโดดไปให้ที่นั่นเลย โดยเราสามารถพิมพ์แค่ไหนก็ได้ ยิ่งพิมพ์ละเอียดก็ยิ่งมีโอกาสเข้าถูกเท่านั้น เช่นจากตัวอย่างข้างบนมะกี้เราสามารถพิมพ์แค่&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;z p
z plat
z platformer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;จะอันไหนก็ได้เลย มันจะเข้าไปที่ platformer แบบเดียวกับข้างบนเป๊ะ&lt;br&gt;&lt;br&gt;
คำถามก็คือถ้าเกิดดันมี platformer กับ platformer-2 แบบนี้มันจะเข้าอันไหน ซึ่งคำตอบก็คือมันจะเข้าอันที่เราเข้าบ่อยกว่านั่นเอง&lt;/p&gt;

&lt;p&gt;แล้วเรายังสามารถที่จะ z ไปที่ไหนก็ได้โดยไม่สนว่าตอนนี้เราอยู่ตรงไหน หรือก็คือถ้าจะไปที่อื่นเราก็สามารถวาร์ปไปยังจุดนั้นได้เลย ราวกับว่าท่านมีความสามารถในการ Teleport เลยทีเดียว&lt;/p&gt;

</description>
      <category>productivity</category>
    </item>
    <item>
      <title>ทำไมถึงเปลี่ยนมาใช้ Colemak?</title>
      <dc:creator>armariya</dc:creator>
      <pubDate>Wed, 11 Mar 2020 03:01:01 +0000</pubDate>
      <link>https://dev.to/armariya/colemak-1hd9</link>
      <guid>https://dev.to/armariya/colemak-1hd9</guid>
      <description>&lt;p&gt;หวังว่าสักวันนึงจะมีคนมาถามคำถามนี้กับผม ฮ่า ๆๆ เพราะฉะนั้นก่อนอื่นเลย ทุกคนจะต้องรู้ว่าอะไรคือ Colemak&lt;/p&gt;

&lt;h3&gt;
  
  
  Colemak Keyboard Layout
&lt;/h3&gt;

&lt;p&gt;Colemak, ออกเสียงว่า /’ko:lmæk/ (Coal-Mac)เป็นรูปแบบการจัดวาง Layout ของคีย์บอร์ดที่ถูกคิดค้นโดย Shai Coalman ในปี 2006 ตอนนี้เป็น Keyboard Layout ยอดนิยมอันดับ 3 โดยอันดับแรกคืออันที่ทุกคนใช้กันอยู่นี่แหละคือ QWERTY ส่วนอันดับสองคือ Dvorak&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8kDKRQ3b--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2Azc2JlicgWjw-439BOQLqBA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8kDKRQ3b--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2Azc2JlicgWjw-439BOQLqBA.png" alt=""&gt;&lt;/a&gt;Heat map ของ Keyboard Layout QWERTY&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Y5YA-B8p--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2ASaG7bF43wCFUeJFWGremwQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Y5YA-B8p--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2ASaG7bF43wCFUeJFWGremwQ.png" alt=""&gt;&lt;/a&gt;Heat map ของ Keyboard Layout Dvorak&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--AoZgCwR5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2A1N0MK8X2fBoVztet3g1eOA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AoZgCwR5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2A1N0MK8X2fBoVztet3g1eOA.png" alt=""&gt;&lt;/a&gt;Heat map ของ Keyboard Layout Colemak&lt;/p&gt;

&lt;p&gt;ข้างบนนี่คือ Heat map ของ Keyboard Layout ของแต่ละแบบ จะเห็นว่าทั้ง Dvorak และ Colemak พยายามจะแก้ปัญหาที่ QWERTY มี คือการที่เราต้องเอื้อมมือไปกดตัวอักษรในแถวบนหรือล่าง แทนที่จะอยู่บน Home Row (แถวกลางนั่นแล มันคือแถวที่เราวางนิ้ว) ซึ่งจริง ๆ แล้วมันยังมี Layout อื่น ๆ อีกมากมายเช่น Workman, Dvorak Programmer และอื่น ๆ อีกมากมาย&lt;/p&gt;

&lt;h3&gt;
  
  
  จุดเริ่มต้น
&lt;/h3&gt;

&lt;p&gt;มาถึงจุดเริ่มต้นของเนื้อเรื่องแล้ว ฮ่า ๆๆ ตั้งแต่เด็กแล้ว ผมมีโอกาสได้เรียนวิชาพิมพ์ดีดตอนมัธยมต้น โดยตอนนั้นยังทันที่เรียนแบบพิมพ์ดีดแบบเครื่องพิมพ์ดีดเลย คิดแล้วก็เป็นโชคดีของผมนะ ที่ได้เรียน รู้สึกเท่ แต่ว่าการเรียนนั้นตอนสอบก็โอเคอยู่นะ แต่พอผ่านมา ผมได้เล่นเกมออนไลน์เกมแรก Ragnarok นั่นเอง เพิ่งเข้าใหม่ ๆ เลย ด้วยความที่ต้องการ Speed ในการพิมพ์ เลยพิมพ์ไปพิมพ์มาเหลือใช้อยู่แค่ แปดนิ้วเท่านั้น (นิ้วก้อยไม่ได้ทำงานนั่นเอง) แล้วก็เป็นแบบนั้นเรื่อยมา จนกระทั่งเมื่อเดือนก่อน..&lt;/p&gt;

&lt;h3&gt;
  
  
  ย่ำอยู่ที่เดิม
&lt;/h3&gt;

&lt;p&gt;ระหว่างที่ผมนั่งทำงานเป็นเป็น Developer อย่างเช่นทุก ๆ วันที่ผ่านมา ซึ่งที่บริษัทของผมเนี่ย นาน ๆ ทีก็จะมีพิธีกรรมอย่างหนึ่ง คือการแข่งพิมพ์กันอย่างไร้เหตุผล นาน ๆ ก็จะเล่นกันสักทีนึง สิ่งที่ผมสังเกตได้ก็คือความเร็วในการพิมพ์ของผมจะอยู่ที่ประมาณ​ 50 wpm มาตั้งแต่ไหนแต่ไร ไม่ว่าจะเล่นผ่านมากี่ปีก็ยัง 50 wpm อยู่นี่แหละ เมื่อเดือนที่แล้วผมก็เลยสงสัยว่าอะไรฟะ ทำไมมันไม่เร็วขึ้นเลย ทั้ง ๆ ที่พิมพ์มาเป็นปี ๆ&lt;/p&gt;

&lt;h3&gt;
  
  
  ก้าวไปข้างหน้า
&lt;/h3&gt;

&lt;p&gt;ผมเลยไปลองเริ่มศึกษาด้วยว่าจะทำยังไงให้พิมพ์เร็วขึ้นได้บ้าง ทุก ๆ ที่ก็บอกแหละนะว่ามันต้องเริ่มจากการพิมพ์สัมผัสนี่แหละ การที่เราเพิ่มเข้าไปอีกสองนิ้ว มันจะช่วยเพิ่มความเร็วได้อีกมาก ก็เลยเอาวะ ไหน ๆ ก็ไหน ๆ ละ ลองฝึกพิมพ์สัมผัสอีกสักรอบละกัน แต่จะเรียนใหม่ทั้งทีเนี่ย ลองดูเรื่อง Keyboard Layout สักหน่อย เริ่มจากดูก่อนเลยว่านอกจาก QWERTY แล้วมีอะไรที่ฮิต ๆ อีกบ้างก็คือเจอแบบข้างบน Dvorak กับ Colemak ที่เป็นอันดับ 2 กับ 3 แล้วก็เลยพบว่าอ้าวสองอันนี้เนี่ยมีให้เปลี่ยนใน Mac อยู่แล้ว ไม่ต้องโหลด Layout มาลงเพิ่มก็เลยสบายละ โดยสิ่งที่สองอันนี้เคลมว่าเหนือกว่า QWERTY ก็คือเรื่องของความ Ergonomic ไม่เจ็บข้อมืออะไรงี้ ก็ไม่รู้ว่าจริงไหมน่ะนะ ฮ่า ๆๆ&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MtBeSejJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AMwqmUd-_UfqTkoXEZhHhBA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MtBeSejJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AMwqmUd-_UfqTkoXEZhHhBA.png" alt=""&gt;&lt;/a&gt;Layout ของ Dvorak&lt;/p&gt;

&lt;p&gt;ตอนแรกใจนี่มาทาง Dvorak มาก จะเห็นว่า Dvorak จะเอาสระพวก a o e i u มาไว้ทางมือซ้ายหมดเลย ซึ่งมันก็ดีไปอีกแบบ แต่ถ้าสังเกตดี ๆ คุณจะพบกับอะไรครับ!!? เอาง่าย ๆ ลองดูตัว C ก็ได้ครับ ลองคิดว่าจะต้อง Copy/Paste ดูครับ! ชีวิตเริ่มลำบากขึ้นมาเลยใช่ไหม และใช่ครับ นั่นคือข้อเสียของ Dvorak หลัก ๆ เลยก็คือ Keyboard Shortcut แย่มาก ๆ กดลำบาก ก็คือพิมพ์ง่ายแหละ แต่ความสะดวกสบายนี่หายไปเพียบ&lt;/p&gt;

&lt;p&gt;อะ ข้อเสียมันหนักหน่วงไปรับไม่ได้ เลยไปลองดู Colemak บ้าง ก็ค้นพบว่า Shai Coleman เนี่ยเขาก็เข้าใจถึงจุดนี้ รวมถึงเข้าใจว่าการเปลี่ยนทั้ง Layout ไปเลยเนี่ย มันช่างไม่น่าเย้าหยวนใจให้เปลี่ยนเอาซะเลย เลยทำการเปลี่ยนคีย์จาก QWERTY เพียงแค่ 17 คีย์เท่านั้น&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dFAn7ZO---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AN8rPZEEKYA52GHPwyK_C4w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dFAn7ZO---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AN8rPZEEKYA52GHPwyK_C4w.png" alt=""&gt;&lt;/a&gt;Layout ของ Colemak&lt;/p&gt;

&lt;p&gt;รวมถึงการเอาตัว shortcut สำคัญ ๆ เนี่ยไว้ทำเดิม ไม่ว่าจะเป็น C, V, W, Q ที่ใช้กันบ่อย ๆ พออ่านคำเคลมแล้วก็เออดูดีอยู่นะ เอาละวะ สักครั้งนึงในชีวิตขอเลือก Layout ที่จะใช้ด้วยตัวเองบ้างเถิด ก็เลยเลือกที่จะฝึก Colemak ดูสักตั้งหนึ่ง&lt;/p&gt;

&lt;h3&gt;
  
  
  การฝึกฝนมันช่างหนักหน่วง
&lt;/h3&gt;

&lt;p&gt;การฝึกฝนก็เริ่มจากการที่ใช้เว็บไซต์ &lt;a href="https://thetypingcat.com/"&gt;https://thetypingcat.com/&lt;/a&gt; ซึ่งเป็นเว็บไซต์ที่ดีมาก ๆ เขาจะค่อย ๆ ให้ฝึกทีละคีย์สองคีย์ โดนไล่ไปทีละแถว ตอนแรก ๆ เนี่ยผมก็เริ่มจากการใช้ QWERTY ในตอนกลางวัน แล้วมานั่งฝึกอันนี้ในตอนกลางคืน ไม่งั้นเดี๋ยวจะเขียนโค้ดได้ช้าเกินงานไม่เสร็จ ฮ่า ๆๆๆ ซึ่งบอกตามตรงว่ามันลำบากมาก ๆ เลยนะกับการเริ่มฝึกแรก ๆ ภาพ Layout มันยังไม่มา ใช้พลังของ muscle memory ล้วน ๆ ตอนที่ฝึก ขอแนะนำคำว่าอย่าก้มลงไปมอง keyboard เพราะจะทำให้ติดเป็นนิสัยในภายหลังได้ ถ้าจะมอง Layout ก็มองอันของในเว็บนั่นแหละครับ มันมีให้อยู่และ จะได้ไม่ต้องก้มครับ&lt;/p&gt;

&lt;p&gt;ตอนแรก ๆ เนี่ยคุณจะพิมพ์ได้แบบสโลว์ไลฟ์มาก ๆ ครับ แบบอารมณ์ 10 wpm แบบนั้นเลย ต้องอดทนครับ ทำอะไรไม่ได้มากกว่านั้น ฮ่า ๆๆ ผมก็ฝึกจนครบทุกตัวอักษรใช้เวลาประมาณสามอาทิตย์ครับ แล้วก็ฝึกพิมพ์มาเรื่อย ๆ จนได้ประมาณ 30 wpm ครับ พอถึงจุดนี้ผมก็เอาวะ น่าจะใช้ทำงานไหวแล้ว ก็เอา QWERTY ออกจาก Keyboard ออกไปแล้วใช้ Colemak ทำงานซะเลย&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;ทำงานช้ามาก ๆ ช้าจนหงุดหงิด : armariya&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;นั่น quote มาจากคำพูดของผมเองที่พูดกับตัวเองด้วยเนี่ยแหละ ฮ่า ๆๆๆ ความเร็วผมก็กระเตื้องขึ้นช้ามาก ๆ แต่ก็ดีขึ้นตามวันเวลาที่เปลี่ยนไปครับ จนประมาณเมื่ออาทิตย์ที่แล้ว ผมได้ลองใช้อีกเว็บหนึ่งก็คือ &lt;a href="https://www.keybr.com/"&gt;https://www.keybr.com/&lt;/a&gt; มันคล้าย ๆ กันแหละครับ แต่ส่วนตัวผมคิดว่าเอาไว้ฝึกตอนที่รู้ Layout หมดแล้วดีกว่าครับ คือเขาทำดีมาก ๆ ถ้าคุณ Login เนี่ย มันจะมี Profile ให้ดูด้วยครับ แบบพิมพ์เร็วขึ้นแค่ไหน พิมพ์ผิดบ่อยไหม พิมพ์ตัวไหนบ่อย ซึ่งดีมาก ๆ เลยครับ ทำให้เราเห็นพัฒนาการของตัวเอง แล้วทำให้มีกำลังใจขึ้นมาก ๆ ครับ&lt;/p&gt;

&lt;p&gt;หลังจากผ่านมาเดือนครึ่งจากจุดเริ่มต้น จนตอนนี้ผมพิมพ์ได้เฉลี่ยนประมาณ 55 wpm แล้วครับ สูงสุดคือ 72 wpm ซึ่งรู้สึกดีมาก ๆ ที่การลงแรงของเรามันประสบความสำเร็จ เย่!&lt;/p&gt;

&lt;p&gt;สรุปทั้งหมดทั้งมวล ถ้าใครอยากพิมพ์เร็วเนี่ย ถ้าไม่อยากเปลี่ยน Layout แนะนำว่ายังไง ๆ ก็ควรที่จะพิมพ์สัมผัสนะครับ แนะนำให้ลองลงแรงดู มันจะช่วยให้พิมพ์ได้เร็วขึ้นเยอะเลยครับ แต่ถ้าใครอยากลองเปลี่ยนมาใช้ Colemak แบบผมละก็บอกเลยว่า Welcome!&lt;/p&gt;

&lt;h4&gt;
  
  
  อ้างอิง
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;ภาพ Heat map ของ Keyboard Layout และภาพ Keyboard — &lt;a href="https://www.patrick-wied.at/projects/heatmap-keyboard/"&gt;https://www.patrick-wied.at/projects/heatmap-keyboard/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;ข้อมูลเกี่ยวกับ Colemak&lt;/li&gt;
&lt;li&gt;เว็บไซต์สำหรับฝึกพิมพ์ — &lt;a href="https://thetypingcat.com/"&gt;https://thetypingcat.com/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;เว็บไซต์สำหรับฝึกพิมพ์หลังจากรู้ Layout แล้ว — &lt;a href="https://www.keybr.com/"&gt;https://www.keybr.com/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>changekeyboardlayout</category>
      <category>touchtypingtips</category>
    </item>
    <item>
      <title>ปฐมบท ก้าวแรกสู่ Category Theory</title>
      <dc:creator>armariya</dc:creator>
      <pubDate>Wed, 13 Nov 2019 15:30:47 +0000</pubDate>
      <link>https://dev.to/armariya/category-theory-2c83</link>
      <guid>https://dev.to/armariya/category-theory-2c83</guid>
      <description>&lt;p&gt;สวัสดีครับ บอกตามตรงว่าหัวข้อนี้เนี่ย ผมเพิ่งจะก้าวเข้ามาเหมือนกันนะครับ อาจจะมีผิดถูกบ้าง แต่จะพยายามให้ถูกที่สุดนะครับ โดยผมจะอ้างอิงจากหนังสือ 'Category Theory for Programmers' ของ 'Bartosz Milewski' เป็นหลักนะครับ เพราะว่าผมกำลังอ่านเล่มนี้อยู่ แล้วพอได้ข้อมูลมาก้อนนึง ผมก็จะพยายามมาเขียนบทความเรื่อย ๆ นะครับ&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kmvHxa8N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2ARZzOkdP9s-6DzlICymw_9Q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kmvHxa8N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2ARZzOkdP9s-6DzlICymw_9Q.png" alt="" width="800" height="644"&gt;&lt;/a&gt;หน้าแรกของหนังสือ Category Theory for Programmers&lt;/p&gt;

&lt;p&gt;สำหรับ Category Theory คืออะไรนั้น ผมขออธิบายสั้น ๆ ว่ามันคือสาขาหนึ่งใน Math ที่เหมาะกับสมองของ Programmer มาก ๆ โดย Category Theory เนี่ย แทนที่เราจะมองเจาะลึกรายละเอียดลงในแต่ละอย่าง แต่มันกลับผลักดันให้เราเลือกที่จะมองภาพรวมของสิ่งต่าง ๆ มากกว่า เอ้อเกือบลืมบอกไป ในบล็อกนี้ผมจะเขียนหรือยกตัวอย่างด้วยภาษา Haskell เป็นหลักนะครับ จะค่อย ๆ Introduce เขาสู่ทุกคนไป ถ้าใครไม่เคยเขียนมาก่อนนะครับ&lt;/p&gt;

&lt;h4&gt;
  
  
  Category: The Essence of Composition
&lt;/h4&gt;

&lt;p&gt;ตัว Category เองมี Concept ที่ง่าย ๆ มากคือ Category ประกอบไปด้วย object(s) ที่มี arrow ที่เชื่อมกันระหว่าง object(s) โดยปกติเวลาเราวาดเรามักจะแทนพวก object(s) ด้วยวงกลมหรือจุดเพียงจุดเดียวก็ได้ แล้วตัว arrow ก็คือ arrow จริง ๆ อะ เป็นลูกศรที่เชื่อม object(s) เข้าด้วยกัน โดยจากชื่อข้างบนเลยก็คือ The Essence of Composition หรือก็คือ Composition เป็นหัวใจหลักของ Category กล่าวคือการ compose ของ arrow ถ้าเรามี arrow จาก object A ไป object B และเรามี arrow จาก object B ไป object C เราสามารถบอกได้ว่ามันจะต้องมี arrow จาก A ไป C แน่ ๆ ที่เกิดจากการ compose กันของ 2 arrows&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--M4qQQJfU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/257/1%2AyyQIEYHgJJTwvNiCxzhu-A.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--M4qQQJfU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/257/1%2AyyQIEYHgJJTwvNiCxzhu-A.jpeg" alt="" width="257" height="314"&gt;&lt;/a&gt;A, B, C แทน object ส่วนลูกศรนั่นคือ arrow (สามารถเรียกได้ว่า morphism) arrow f กับ g ส่วนเส้นประม่วงคือ เส้นที่เกิดจากการ compose กันของสอง arrow เป็น arrow ใหม่ เรียกว่า f∘gอ่านว่า ‘f after g’&lt;/p&gt;

&lt;p&gt;สับสนล่ะสิ?? 555 ไม่ต้องแปลกใจครับ เป็นเรื่องปกติมาก ๆ ที่จะมึน ๆ แต่ด้วยความที่เราเป็น Programmer ทำให้เรามองเรื่องนี้ได้ง่ายขึ้นมาก ๆ ครับ โดยปกติแล้วพวกเราเหล่า Programmer อยู่ในโลกที่เรียกว่า ‘Category of Types” ถ้าเราลองเอาความหมายข้างบนมาแปลเป็นภาษา Programmer ก็คือ เราสามารถมองพวก object(s) ที่ว่าเนี่ยเป็นแต่ละ type ได้เช่น Int, String, Boolean อะไรแบบนั้น แล้วพวก arrow เนี่ยก็คือ function ที่เชื่อม object(s) พวกนี้เข้าด้วยกัน เช่น function ที่รับ Int เข้าไปแล้ว return String กลับมา ก็จะเป็น Int -&amp;gt; String เป็นหน้าตาประมาณนี้ ขอแวะกลับมาดู Haskell นิดนึงครับ ในกรณีนี้เราจะสามารถเขียนใน Haskell ได้ประมาณนี้&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;f :: Int -&amp;gt; String
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ข้างบนนี้อ่านว่า function f มี type เป็น Int -&amp;gt; String หรือก็คือรับ Int เข้าไป แล้ว return String กลับออกมานั่นเอง ที่นี้เรามาดูการ compose ของ arrow กัน สมมติเรามีแบบนี้&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;f :: String -&amp;gt; Int
g :: Int -&amp;gt; Bool

g.f :: String -&amp;gt; Bool
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;จะเห็นว่าเรามีฟังก์ชัน f ที่รับ String เข้ามาแล้วส่ง Int กลับมา&lt;br&gt;&lt;br&gt;
และเรามีฟังก์ชัน g ที่รับ Int เข้ามาแล้วส่ง Bool กลับมา&lt;br&gt;&lt;br&gt;
ทีนี้เราจะเห็นอะไรแปลก ๆ ก็คือ g.f ตัว g.f นี่จะหมายถึง g compose กับ f โดยเราจะอ่านว่า ‘g after f’ หรือก็คือทำ f ก่อนแล้วค่อยทำ g นั่นเอง ซึ่งก็จะมี type เป็น String -&amp;gt; Bool เลย&lt;/p&gt;
&lt;h4&gt;
  
  
  แล้วอะไรถึงจะถือว่าเป็น Category?
&lt;/h4&gt;

&lt;p&gt;การที่เราจะรู้ได้ว่าอะไรเป็น Category เนี่ย การ Compose ใน Category นั้น ๆ จะต้องมีคุณสมบัติทั้งหมดสองอย่างคือ&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Composition Associative&lt;/li&gt;
&lt;li&gt;Identity arrow&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ถ้ามีครบทั้งสองอย่างแล้วเราจะถือว่าสิ่งนั้นเป็น Category โดยแต่ละอันคือ&lt;/p&gt;
&lt;h4&gt;
  
  
  Composition Associative
&lt;/h4&gt;

&lt;p&gt;สมมติว่าถ้าคุณมี 3 arrow f, g และ h อยู่ ซึ่งสามารถ Compose กันได้แล้วเนี่ย ไม่ว่าจะใส่วงเล็บหรือไม่ใส่วงเล็บ ก็ไม่มีผลต่อการ Compose ถ้าเขียนให้ออกมาในรูปของสัญกรณ์คณิตศาศตร์แล้วจะได้แบบนี้&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;h∘(g∘f) = (h∘g)∘f = h∘g∘f
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8C5SXsVJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/428/1%2AyG7Z_1bENrsZp2uQpG6nMA.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8C5SXsVJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/428/1%2AyG7Z_1bENrsZp2uQpG6nMA.jpeg" alt="" width="428" height="384"&gt;&lt;/a&gt;จากรูปจะเห็นว่าไม่ว่าจะ compose อันไหนก่อนหรือ compose ทั้งหมดเลย ปลายทางก็ยังคงเหมือนกัน&lt;/p&gt;

&lt;h4&gt;
  
  
  Identity arrow
&lt;/h4&gt;

&lt;p&gt;สำหรับทุก object ใน Category จะต้องมีอยู่ arrow นึงที่จะลูปเข้าหาตัวเอง ซึ่งหมายความว่าถ้าเราเอา arrow นี้ Compose เข้ากับ arrow อื่น ๆ จะต้องได้ arrow นั้น ๆ เสมอ ซึ่งเราจะเรียก arrow ที่รูปเข้าหาตัวเองว่า Identity arrow&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0B64EFrw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/79/1%2AoOgksly9CP0OxD3HQLyB-Q.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0B64EFrw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/79/1%2AoOgksly9CP0OxD3HQLyB-Q.jpeg" alt="" width="79" height="154"&gt;&lt;/a&gt;ภาพของ identity arrow&lt;/p&gt;

&lt;p&gt;ถ้าเราเขียนให้อยู่ในรูปของสัญกรณ์คณิตศาสตร์จะได้เป็น&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;IDA ∘ f = f
f ∘ IDA = f
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;เราอาจจะคิดไม่ออกว่าถ้าอยู่ในรูปของ Function จะเป็นไง มันก็คือฟังก์ชันที่รับ Argument ใด ๆ เข้ามาแล้วจะ return Argument กลับไปนั่นเอง&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Identity :: A =&amp;gt; A
Identity x = x
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;อันนี้ก็คือ identity function มี type เป็นรับอะไรมาก็ได้แล้วส่ง type เดิมกลับไป ถ้าเราจะเขียนในภาษาอื่นก็อาจจะต้องทำด้วย Generic Type อะไรก็ว่าไปที่รับ Type มาด้วยจะได้ return ถูก รับ Int คืน Int, รับ String คืน String&lt;/p&gt;

&lt;p&gt;จบแล้ว เย่! ใช่ครับฟังไม่ผิด Category Theory โดยเนื้อแท้มันมีแค่นี้แหละ แต่มันจะเรื่องที่ละเอียดไปมากกว่านี้อีก ไว้เรามาต่อกันในบล็อกหน้านะครับ ถ้าใครมีข้อสงสัยหรืออะไรยังไงทักมาได้เลยนะครับผม&lt;/p&gt;

</description>
    </item>
    <item>
      <title>เบิกเนตรวิธีเขียน Plugin Android บน Unity3d ด้วยไฟล์ Kotlin โดยตรง</title>
      <dc:creator>armariya</dc:creator>
      <pubDate>Mon, 24 Jun 2019 06:07:38 +0000</pubDate>
      <link>https://dev.to/armariya/plugin-android-unity3d-kotlin-g06</link>
      <guid>https://dev.to/armariya/plugin-android-unity3d-kotlin-g06</guid>
      <description>&lt;p&gt;สวัสดีครับ กลับมาอีกครั้งกับการเขียน Plugin Android นะครับ หลังจากที่เขียนไปในบล็อก&lt;a href="https://dev.to/armariya/-plugin-android--unity3d--kotlin--onactivityresult--override-activity--3l8m"&gt;ที่แล้ว&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;เนื่องจากผมเพิ่งได้ไปเห็น Feature หนึ่งที่เขาเขียนไว้ใน Document แต่ไม่มีใครพูดถึงเอาซะเลย เลยถือโอกาสนี้ลองดูครับ ถ้าใครอยากอ่านเต็ม ๆ เข้าที่ &lt;a href="https://docs.unity3d.com/Manual/AndroidJavaSourcePlugins.html" rel="noopener noreferrer"&gt;https://docs.unity3d.com/Manual/AndroidJavaSourcePlugins.html&lt;/a&gt; นี้ได้เลยครับ&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AOz2LAWFaRuoNeoN1sTQ-tw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AOz2LAWFaRuoNeoN1sTQ-tw.png"&gt;&lt;/a&gt;หน้าบอก Feature การเอาไฟล์ Kotlin/Java มาทำเป็น Plugin&lt;/p&gt;

&lt;p&gt;สำหรับวันนี้จะเป็นการเขียน Plugin ที่เอาไว้สำหรับปริ้นท์​ Toast ขึ้นมาบนหน้าจอ Android นะครับ&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prerequisite&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Unity3d 2019.2.0b6 (จริง ๆ 2019.1 ก็ได้ครับ แต่พอดีตอนทำใช้เวอร์ชันนี้พอดี)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;เริ่มด้วยการสร้างโปรเจคขึ้นมาอันนึงครับ อันนี้ตั้งชื่อว่า ToastDemo เลือกเป็น 2D ก็พอครับ เพราะว่าเราไม่ได้ทำอะไรมาก เสร็จแล้วก็ CREATE โลด&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AucDXCq1BxYx7MBcO3ieYwQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AucDXCq1BxYx7MBcO3ieYwQ.png"&gt;&lt;/a&gt;หน้าตาตอนสร้างโปรเจค&lt;/p&gt;

&lt;p&gt;หลังจากเข้ามาในโปรเจคนะครับ ไปที่ File -&amp;gt; Player Settings แล้วเลือก Android แล้วกด Switch Platform โลดเลยครับ&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AlTar6YzA4BWnGvbWLb0Few.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AlTar6YzA4BWnGvbWLb0Few.png"&gt;&lt;/a&gt;Switch Platform เป็น Android&lt;/p&gt;

&lt;p&gt;หลังจาก Switch มาเสร็จสรรพ กด Player Settings ข้างซ้ายต่อเลยครับ แล้วเข้าไปที่ส่วนของ Other Settings เพื่อเซ็ต Bundle Identifier ของโปรเจคนี้ก่อนนะครับ อันนี้ผมตั้งไปว่า dev.warpgate.ToastDemo นะครับ (จริง ๆ จะเป็นอะไรก็ได้นะ ไม่ต้องเป็นอันนี้ก็ได้)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AL0HvJp2-HtzxGmfAnNTCIA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AL0HvJp2-HtzxGmfAnNTCIA.png"&gt;&lt;/a&gt;เซ็ต Bundle Identifier ของโปรเจ็ค&lt;/p&gt;

&lt;p&gt;ต่อไปเราจะมาสร้าง Folder สำหรับเก็บโค้ด Kotlin ก่อนนะครับ โดย***ย้ำสำคัญมาก ๆ เลยนะครับ คือ ห้ามเอาไว้ใน Folder พิเศษของ Unity อันต่าง ๆ นะครับ เพราะว่าถ้าอยู่ใน Folder พิเศษ Unity จะไม่เอาโค้ดเราเข้าไปด้วยนะครับ โดยอันนี้สร้างเป็น Natve/Android นะครับ&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F398%2F1%2Aum7c6cxwVRNQdKox3rRocw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F398%2F1%2Aum7c6cxwVRNQdKox3rRocw.png"&gt;&lt;/a&gt;Folder structure ของโปรเจค&lt;/p&gt;

&lt;p&gt;ทำการเปิดโปรเจคข้างนอกแล้วสร้างไฟล์ Native.kt ได้เลยครับ แล้วทำการเปิด File ขึ้นมาแล้วใส่โค้ดนี้โลด&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Native.kt

package dev.WarpGate.WarpGate


import android.content.Context

import android.widget.Toast


class _Native_ {

  fun toastShort(context: _Context_, text: _String_) {

    _Toast_.makeText(context, text, _Toast_._LENGTH\_SHORT_).show()

  }


  fun toastLong(context: _Context_, text: _String_) {

    _Toast_.makeText(context, text, _Toast_._LENGTH\_LONG_).show()

  }

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ซึ่งโค้ดนี้ก็คือเป็นโค้ดธรรมดาใน Android ที่เอาไว้ใช้สำหรับการเรียก Toast ขึ้นมาเลยนะครับ สังเกตนะครับว่าชื่อ package ข้างบนนี่จะเหมือนกับของตัว Bundle Identifier เลยนะครับ&lt;/p&gt;

&lt;p&gt;หลังที่เขียนเสร็จแล้ว เราจะต้องไปเลือกครับ ว่าจะให้ไฟล์นี้ Import บน Platform ไหนบ้างนะครับ ซึ่งแน่นอนเราจะต้องเลือก Android เพียงอย่างเดียว โดยเราสามารถคลิกที่ไฟล์ แล้วเลือกในหน้าต่าง Inspector ได้เลยครับ&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F810%2F1%2AlCwtGBKU5dXxQgS-b1tvHg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F810%2F1%2AlCwtGBKU5dXxQgS-b1tvHg.png"&gt;&lt;/a&gt;ปรับ Platform ให้เหลือแต่ Android&lt;/p&gt;

&lt;p&gt;หลังจากนั้นเราต้องทำการเขียนไฟล์ C# ที่จะไปเรียก Plugin นี้ผ่าน Unity นะครับ ทำการสร้าง Scripts/Native/Native.cs ได้เลยครับผม&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2Ad_j2-qOL7UkD__MwbUG2Aw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2Ad_j2-qOL7UkD__MwbUG2Aw.png"&gt;&lt;/a&gt;สร้างไฟล์ C# ขึ้นมาเพื่อใช้เรียก Plugin&lt;/p&gt;

&lt;p&gt;จากนั้นก็ใส่โค้ด Monobehaviour ได้เลยนะครับ&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Native.cs

using UnityEngine;

public class _Native_ : MonoBehaviour {

  private const string m\_UnityPlayerClass = "com.unity3d.player.UnityPlayer";
  private const string m\_NativeClass = "dev.warpgate.ToastDemo.Native";
  private const string m\_ToastShortMethod = "toastShort";
  private const string m\_ToastLongMethod = "toastLong";

  public void ToastShort(string text) {
    using (AndroidJavaClass unityPlayer = new AndroidJavaClass(m\_UnityPlayerClass)) {
      using (AndroidJavaObject currentActivity = unityPlayer.GetStatic&amp;lt;AndroidJavaObject&amp;gt;("currentActivity")) {
        using (AndroidJavaObject context = currentActivity.Call&amp;lt;AndroidJavaObject&amp;gt;("getApplicationContext")) {
          using (AndroidJavaObject native = new AndroidJavaObject(m\_NativeClass)) {
            native.Call(m\_ToastShortMethod, context, text);
          }
        }
      }
    }
  }

  public void ToastLong(string text) {
    using (AndroidJavaClass unityPlayer = new AndroidJavaClass(m\_UnityPlayerClass)) {
      using (AndroidJavaObject currentActivity = unityPlayer.GetStatic&amp;lt;AndroidJavaObject&amp;gt;("currentActivity")) {
        using (AndroidJavaObject context = currentActivity.Call&amp;lt;AndroidJavaObject&amp;gt;("getApplicationContext")) {
          using (AndroidJavaObject native = new AndroidJavaObject(m\_NativeClass)) {
            native.Call(m\_ToastLongMethod, context, text);
          }
        }
      }
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;หลังจากนั้นลองเอา Function พวกนี้ไปใส่ในปุ่มแล้ว Build ลง​ Android มาลองได้เลยครับ&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F776%2F1%2A_yHuKpF_hA8Y64Fj6wb4uw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F776%2F1%2A_yHuKpF_hA8Y64Fj6wb4uw.png"&gt;&lt;/a&gt;หน้าจอตอนใส่โค้ดลงในปุ่มนะครับ&lt;/p&gt;

&lt;p&gt;ในที่สุดดด เราก็มาถึงเวลานี้ครับ เย่ พอเทสแล้วก็จะได้แบบนี้นะครับบ&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AGxn7S4YcvMZ3cebxuNA5Pw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AGxn7S4YcvMZ3cebxuNA5Pw.png"&gt;&lt;/a&gt;หน้าตา Demo ของผลสำเร็จ&lt;/p&gt;

&lt;p&gt;จะเห็นว่า มี Toast ขึ้นมาด้านล่างครับ ถือว่าประสบความสำเร็จอย่างงดงาม เย่&lt;/p&gt;

&lt;p&gt;จบไปแล้วนะครับ สำหรับการเขียน Plugin Android ด้วยไฟล์ Kotlin นะครับ ถ้าเกิดว่ามีใครไม่เข้าใจตรงไหน หรือทำตามแล้วไม่ได้ Comment ไว้ด้านล่างนี้ได้เลยนะครับ :)&lt;/p&gt;

</description>
      <category>unity3d</category>
      <category>unity</category>
      <category>plugindevelopment</category>
      <category>android</category>
    </item>
    <item>
      <title>ทำความรู้จักกับ Lazy Evaluation</title>
      <dc:creator>armariya</dc:creator>
      <pubDate>Wed, 13 Feb 2019 04:38:00 +0000</pubDate>
      <link>https://dev.to/armariya/-lazy-evaluation-1n4n</link>
      <guid>https://dev.to/armariya/-lazy-evaluation-1n4n</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fee16yvauxc434e3vdxso.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fee16yvauxc434e3vdxso.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;ไม่รู้ว่าหลาย ๆ คนเคยได้ยินคำนี้มาก่อนรึเปล่า แต่ว่าผมเพิ่งเคยรู้จักและได้ยินมาจากการอ่านหนังสือ &lt;a href="https://doc.rust-lang.org/book/" rel="noopener noreferrer"&gt;“the book,” The Rust Programming Language&lt;/a&gt; มันเป็นหนังสือของภาษาโปรแกรม Rust ซึ่งเป็นหนังสือที่ดีมาก ๆ ผมแนะนำให้ทุกคนนั้นน ควรจะไปอ่านครับ บอกเลยว่าเด็ด!&lt;/p&gt;

&lt;h4&gt;
  
  
  Lazy Evaluation คืออะไร?
&lt;/h4&gt;

&lt;p&gt;“Lazy” ก็คือแบบโคตรจะตรงตัว ขี้เกียจ นั่นละครับ Lazy Evaluation คือจะทำการคำนวณบางสิ่งบางอย่างในเวลาที่เราต้องการค่ามันเท่านั้น เน้นย้ำว่าเท่านั้นนะครับ ลองดูโค้ดชุดนี้ครับ&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fn simulated\_expensive\_calculation(result: u32) -&amp;gt; u32 {
  thread::sleep(Duration::from\_secs(2));
  println!("Finished");
  result
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;เป็นการจำลอง function ที่มีการคำนวณอย่างหนักหน่วง ใช้เวลาถึงสองวินาที ถ้าเราเรียกฟังก์ชันนี้บ่อย ๆ และเรียกแล้วไม่ได้ใช้ค่า ๆ นี้ จะส่งผลกระทบต่อ performance มาก ๆ&lt;/p&gt;

&lt;p&gt;โดย Lazy Evaluation มักจะมาคู่กับ Memorization ซึ่งก็คือพอเราทำการคำนวณค่าแล้วเนี่ย เราจะทำการจำค่าไว้ด้วย ถ้า Parameter เข้ามาเหมือนเดิม เราก็จะ Return ค่าที่มีให้ไปเลย โดยไม่ต้องไปทำการคำนวณใหม่&lt;/p&gt;

&lt;h4&gt;
  
  
  วิธีการทำ Lazy Evaluation
&lt;/h4&gt;

&lt;p&gt;คราวนี้เราจะมาปรับปรุงโค้ดชุดด้านบน ให้กลายเป็น Lazy Evaluation + Memorization กันนะครับ เริ่มต้นโดยการเปลี่ยนโค้ดชุดข้างบนจาก function ให้กลายเป็น closure ซะก่อน&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let expensive\_closure = |result: u32| -&amp;gt; u32 {
  thread::sleep(Duration::from\_secs(2));
  println!("Finished");
  result
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ตรงนี้นี่คือตัวฟังก์ชันเราจะยังไม่ได้ทำงานนะครับ เป็นแค่การเก็บ function ไว้ในตัวแปรเฉย ๆ นะครับ ยังไม่ได้มีการคำนวณอะไร ที่นี้เราจะสร้าง struct ขึ้นมาที่มีชื่อว่า Cacher เพื่อใช้เป็นที่เก็บ closure กับตัวที่ cache result ไว้นะครับ&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;struct Cacher&amp;lt;T&amp;gt; where T: Fn(u32) -&amp;gt; u32 {
  calculation: T,
  values: HashMap&amp;lt;u32, u32&amp;gt;
}

impl&amp;lt;T&amp;gt; Cacher&amp;lt;T&amp;gt; where T: Fn(u32) -&amp;gt; u32 {
  fn new(calculation: T) -&amp;gt; Cacher&amp;lt;T&amp;gt; {
    Cacher {
      calculation,
      values: HashMap::new(),
    }
  }

  fn value(&amp;amp;mut self, arg: u32) -&amp;gt; u32 {
    match self.value.get(&amp;amp;arg) {
      Some(&amp;amp;v) =&amp;gt; v,
      None =&amp;gt; {
        let v = (self.calculation)(arg);
        self.value.insert(arg, v);
        v
      }
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;จะเห็นได้ว่า Struct นี้มีตัวแปรสองตัวก็คือ calculation ที่เป็นตัวเก็บ closure การทำงาน กับตัว values ที่เอาไว้ map ระหว่าง argument กับ result นะครับ&lt;/p&gt;

&lt;p&gt;จากนี้เนี่ยเวลาใช้งานใช่มั้ยครับมันจะกลายเป็น&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let cacher = Cacher::new(|result: u32| -&amp;gt; u32 {
  thread::sleep(Duration::from\_secs(2));
  println!("Finished");
  result
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ทำการสร้าง cacher ขึ้นมาก่อน เสร็จปุ๊บเวลาเราจะใช้ก็จะใช้แค่&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cacher.value(20)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;แบบนี้เป็นต้น มันก็จะวิ่งไปเข้า function value ข้างบน ถ้าหากว่าเคยประมวลผลค่า argument นี้แล้วมันจะ return คำตอบให้ไปเลย แต่ถ้ายังไม่เคยมีจะค่อยทำการคำนวณเพื่อให้ฟังก์ชันถูกเรียกใช้งานน้อยที่สุดนะครับ&lt;/p&gt;

&lt;p&gt;จะเห็นว่ามันมีประโยชน์เอามาก ๆ เลยนะครับ สามารถทำให้ performance ของโปรแกรมเราดีขึ้นได้มาก ๆ เลยครับ แต่จากที่เห็นข้อมูล implement ข้างบนเนี่ย เราก็จะเห็นข้อเสียของวิธีนี้อยู่เหมือนกัน นั่นก็คือถ้าเราทำการเรียกด้วย argument ที่ต่าง ๆ กันมากเกินไป จะทำให้ต้องคำนวณทุกรอบ จบแทบจะไม่ต่างจากการเขียนธรรมดา แถมยังกิน memory มากกว่าวิธีธรรมดาเสียอีก! เพราะฉะนั้นจะใช้ก็ตรวจสอบปัญหาของตัวเองดี ๆ กันก่อนนะครับ&lt;/p&gt;

&lt;p&gt;ปล. เย่ ถึงช่วงเวลาโฆษณา ตอนนี้กำลังจะเปิด Podcast ใหม่ครับ เย่ ~ ชื่อว่า “Deep Magic” ครับ ที่มาของมันก็คือไปเจอคำ ๆ นี้ใน Wikipedia มาว่าแบบนี้&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Deep magic&lt;/strong&gt; refers to techniques that are not widely known, and may be deliberately kept secret.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;เลยสนใจในคำ ๆ นี้ขึ้นมาครับ เลยจะทำ Podcast อันนี้ขึ้นมาเป็นตอนสั้น ๆ นะครับ ที่จะเอา Technique ที่ได้พบเจอมาระหว่างทำงานเนี่ย มาแชร์ให้รู้จักกันนะครับ สามารถไปติดตามได้ที่ &lt;a href="https://anchor.fm/ariya-lawanitchanon" rel="noopener noreferrer"&gt;https://anchor.fm/ariya-lawanitchanon&lt;/a&gt; นี่เลยนะครับ สำหรับช่องทางอื่น ๆ จะทยอยตามมานะครับ เพราะว่า ตอนไม่พอ submit ไม่ผ่านนะฮะ (ฮา) นั่นแหละครับ ฝากตัวด้วยนะครับ :) ใครจะมาแจมนี่หลังไมค์มาเลยนะ ยินดีมาก&lt;/p&gt;

</description>
      <category>programming</category>
      <category>rust</category>
      <category>lazyevaluation</category>
    </item>
    <item>
      <title>นำแนะแอป “The History of Everything” ที่จะพาเราไปเรียนรู้ประวัติศาสตร์ของโลกใบนี้!</title>
      <dc:creator>armariya</dc:creator>
      <pubDate>Wed, 09 Jan 2019 05:52:54 +0000</pubDate>
      <link>https://dev.to/armariya/-the-history-of-everything--2mke</link>
      <guid>https://dev.to/armariya/-the-history-of-everything--2mke</guid>
      <description>&lt;p&gt;สวัสดีครับบบ วันนี้จะพามาแนะนำให้รู้จักแอปสุดเทพในการเล่าเรื่องที่สุดจะสวยงามนะครับบ แอปนี้ชื่อว่า The History of Everything ครับ เป็นที่บอกประวัติในเรื่องต่าง ๆ ตั้งแต่จุดกำเนิดของจักรวาล ยันเรื่องราวในแต่ละยุค โดยมีการแบ่งตามปีอย่างชัดเจน ทำให้เราเห็นภาพ ไทม์ไลน์ที่ผ่านมาของโลกมากขึ้นครับ&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lf85MBCT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AEhLtx6Ef9rVBYkU6Cy1BYg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lf85MBCT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2AEhLtx6Ef9rVBYkU6Cy1BYg.png" alt=""&gt;&lt;/a&gt;รูปที่ 1 หน้าแรกของแอป&lt;/p&gt;

&lt;p&gt;จากรูปที่ 1 นะครับคือหน้าแรกของแอป รูปลักษณ์สวยงาม และแบ่งหัวข้อไว้อย่างชัดเจนทำให้หาเรื่องที่ต้องการได้ง่าย หรือจะเลื่อนตามไทม์ไลน์ไปเลยก็ได้ครับ&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--cLYzlvAF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2APLFTPxqUioMNIhAteZ3COw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--cLYzlvAF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2APLFTPxqUioMNIhAteZ3COw.png" alt=""&gt;&lt;/a&gt;รูปที่ 2 หน้าไทม์เลยของแอป&lt;/p&gt;

&lt;p&gt;เมื่อเข้ามาก็จะเป็นแบบนี้ครับ มีไทม์ไลน์ทางด้านซ้ายชัดเจน มีหัวเรื่องแปะตรงที่เกิด บวกกับที่เทพมาก ๆ คือ แอนิเมชันที่เขาทำนี่ ลื่นไหล สวยงาม มาก ๆ ครับ&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--O1e0e2Nc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2A_j0lHQaDmQZJmkJoisBe6A.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--O1e0e2Nc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2A_j0lHQaDmQZJmkJoisBe6A.png" alt=""&gt;&lt;/a&gt;รูปที่ 3 หน้าอ่านประวัติศาสตร์ในแต่ละเรื่อง&lt;/p&gt;

&lt;p&gt;พอเราเลือกหัวข้อเข้ามา เราก็จะเจอหน้าเล่าเรื่องประวัติศาสตร์นะครับ อ่านง่าย แต่ผมคิดว่าอย่างน้อย ๆ น่าจะมี Dark Mode มาให้หน่อย มันช่างแสบตาเหลือเกิน 555&lt;/p&gt;

&lt;p&gt;ถ้าใครสนใจสามารถไปดาวน์โหลดได้ทั้ง Android และ iOS เลยนะครับ&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Android&lt;/strong&gt;  — &lt;a href="https://play.google.com/store/apps/details?id=com.twodimensions.timeline"&gt;https://play.google.com/store/apps/details?id=com.twodimensions.timeline&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;iOS — &lt;a href="https://itunes.apple.com/us/app/the-history-of-everything/id1441257460?mt=8"&gt;https://itunes.apple.com/us/app/the-history-of-everything/id1441257460?mt=8&lt;/a&gt;&lt;/p&gt;

</description>
      <category>appreview</category>
    </item>
    <item>
      <title>เขียน Plugin Android สำหรับ Unity3d ด้วย Kotlin เพื่อรับผลจาก onActivityResult โดยไม่ต้อง override activity หลัก</title>
      <dc:creator>armariya</dc:creator>
      <pubDate>Wed, 18 Jul 2018 17:09:58 +0000</pubDate>
      <link>https://dev.to/armariya/-plugin-android--unity3d--kotlin--onactivityresult--override-activity--3l8m</link>
      <guid>https://dev.to/armariya/-plugin-android--unity3d--kotlin--onactivityresult--override-activity--3l8m</guid>
      <description>&lt;p&gt;สวัสดีครับบ วันนี้มาเป็นเรื่องเกี่ยวกับการเขียน Plugin Android บน Unity3d ที่ตัดสินใจมาเขียนนี่เพราะว่า รู้สึกว่าตอนผมทำเนี่ยมันหาอ่านยากมากเลย เลยมาเขียนเก็บไว้ตรงนี้ เพื่อคนต่อ ๆ ไปนะครับบ เดี๋ยวจะทำให้ดูคร่าว ๆ นะครับไม่งั้นมันจะยาวโคตร&lt;/p&gt;

&lt;h4&gt;
  
  
  Requirement สำหรับวันนี้
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Android studio&lt;/li&gt;
&lt;li&gt;Unity3d 2018.2.0f2&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ครับผม สำหรับคนที่มีครบทุกอย่างแล้ว ก็เริ่มกันเลย! โดยสำหรับวันนี้จะมาทำ Line Login กันดูนะครับ&lt;/p&gt;

&lt;p&gt;เปิด Android Studio ขึ้นมาทำงานกันโลด ทำการสร้างโปรเจคบน Andriod แล้ว New Module ใหม่ขึ้นมาเป็น Android Library สักหนึ่งที&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jCTmH-VX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1012/1%2AsCTHodnDaMBiur2lXj1RRA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jCTmH-VX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1012/1%2AsCTHodnDaMBiur2lXj1RRA.png" alt=""&gt;&lt;/a&gt;สร้าง Moudle ใหม่เป็น Android Library&lt;/p&gt;

&lt;p&gt;จากสร้าง Activity ใหม่ชื่อว่า LineSDKActivity ขึ้นมาครับ จากนั้นเราจะมาเริ่มเขียนตัว Plugin กันนะครับ ก่อนอื่น เราต้องไป Download ตัว LineSDK สำหรับ Android จากหน้าเว็บกันก่อนเลยครับ​ (จัดการสร้าง Project อะไรให้เสร็จสรรพเลยนะครับ ลองอ่านจากหน้าเว็บมันดูนั่นแหละ) สามารถหาได้จาก&lt;a href="https://developers.line.me/en/"&gt;ที่นี่&lt;/a&gt; หลังจากเสร็จแล้วก็เอาตัว .aar ลากลง lib โลดเลยครับ&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PJteLap6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/184/1%2ADcYFSbtKvMNOsnfMlBZa7Q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PJteLap6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/184/1%2ADcYFSbtKvMNOsnfMlBZa7Q.png" alt=""&gt;&lt;/a&gt;เอา Line SDK for Android ใส่ในโปรเจค&lt;/p&gt;

&lt;p&gt;จากนั้นก็เขียนโค้ดสำหรับ Login เลยครับผม สามารถแทบจะลอกจากหน้าเว็บของ Developer Line ได้เลยครับ&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class LineSDKActivity : AppCompatActivity() {

    companion object {
        lateinit var lineLoginCallback: LineLoginCallback

        fun startLineSDKActivity(activity: Activity, lineLoginCallback: LineLoginCallback) {
            LineSDKActivity.lineLoginCallback = lineLoginCallback;
            val intent = Intent(activity._applicationContext_, LineSDKActivity::class._java_)
            activity.startActivity(intent);
        }
    }

    data class PlayerProfile(
            val displayName: String,
            val statusMessage: String?,
            val userId: String,
            val pictureUri: Uri?
    )

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        login()
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (requestCode != Constants.REQUEST\_CODE) {
            Log.e("ERROR", "Unsupported Request");
            lineLoginCallback.onLoginFailed("Unsupported Request")
            finish()
        }

        if (data == null) {
            lineLoginCallback.onLoginFailed("Can't connect with Line")
            finish()
        }

        val result: LineLoginResult = LineLoginApi.getLoginResultFromIntent(data)

        when (result._responseCode_) {
            LineApiResponseCode.SUCCESS -&amp;gt; {
                lineLoginCallback.onLoginSuccess(
                        result._lineProfile_!!._displayName_  
)
            }
            else -&amp;gt; {
                lineLoginCallback.onLoginFailed("Login Unsuccessful")
            }
        }

        finish()

    }

    fun login() {
        startActivityForResult(getLineLoginIntent(), Constants.REQUEST\_CODE)
    }

    private fun getLineLoginIntent() : Intent {
        return LineLoginApi.getLoginIntent(this._applicationContext_, Constants.CHANNEL\_ID)
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;หลังจากได้ประมาณนี้ปุ๊บ จะเห็นว่าผมมีใช้ตัว LineLoginCallback เพื่อให้สามารถเอาไปเชื่อมกับตัว C# ใน Unity3d ได้ ก็ทำการสร้างไฟล์ใหม่แล้วประกาศ interface โลด&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;interface LineLoginCallback {
    fun onLoginSuccess(displayName: String)
    fun onLoginFailed(errorMessage: String)
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;หลังจากเสร็จ อย่าลืมว่าต้องใส่ appcompat กับ customtabs ให้กับ build.gradle ของตัว ​module นี้ด้วยนะครับ แบบนี้&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dependencies {
    implementation fileTree(dir: 'libs', include: ['\*.jar'])
    implementation 'androidx.appcompat:appcompat:1.0.0-beta01'
    implementation 'androidx.browser:browser:1.0.0-beta01'
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin\_version"
    implementation(name: 'line-sdk-4.0.8', ext: 'aar')
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;แล้วก็ทำการ build โลดเลยครับ ถ้า build สำเร็จเราก็จะได้เป็นไฟล์ .aar มาอยู่ใน output ของ module นะครับ&lt;/p&gt;

&lt;p&gt;คราวนี้เรามาดูที่ตัว Unity ของเรากันบ้าง ที่นี่เราต้องทำการสร้างโฟลเดอร์ Plugins/Android ขึ้นมาครับ เพื่อเป็นที่เก็บตัว Plugin ของเราตามกฏของ Unity นะครับ&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--40GdITJh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/115/1%2AC1D1DaKz4r8Vrtpc9aqGXw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--40GdITJh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/115/1%2AC1D1DaKz4r8Vrtpc9aqGXw.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;จากนั้นเอา .aar ของเรายัดเข้าไปเลยครับ รวมถึงไฟล์ line-sdk-4.0.8.aar ที่โหลดมาจากเว็บของ Line ด้วยนะครับ จากนั้น เราจะทำการเปิด gradle ไฟล์ขึ้นมา เพื่อจัดการตัว Dependencies ในโปรเจคนะครับ ไปที่ build setting ของ Android ในส่วนของ Publishing Settings ทำการติ้กถูกตรง Custom Gradle Template โลดเลยครับ ตามรูปข้างล่าง&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--819Jtkec--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/298/1%2AUT0meoBtzzCeawTlwJYT-A.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--819Jtkec--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/298/1%2AUT0meoBtzzCeawTlwJYT-A.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;เราจะได้ไฟล์ mainTemplate.gradle ขึ้นมานะครับแล้วทำการ แก้ไขโลดเลยครับ&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;implementation(name:'line-sdk-4.0.8', ext:'aar')
implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.2.51'
implementation 'androidx.appcompat:appcompat:1.0.0-alpha1'
implementation 'androidx.browser:browser:1.0.0-alpha1'
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;ทำการ add สี่ตัวนี้เข้าไปที่ส่วนของ Dependencies ได้เลยครับ จากนั้น ผมจะเขียนคร่าว ๆ ของส่วนการใช้ให้ดูนะครับ จะประมาณนี้ก็คือ ก่อนอื่นสร้างไฟล์ขึ้นมาก่อน สำหรับผมใช้ชื่อ LineSDK.cs นะครับ&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// LineSDK.cs

public class LineSDK {
  public class LineLoginCallback : AndroidJavaProxy {
    public LineLoginCallback : base("xxx.xx.xxx.LineSDKActivity") {}

    public void onLoginSuccess(string displayName) {
      Debug.Log("Line login success");
    }

    public void onLoginFailed(string errorMessage) {
      Debug.Log("Line login failed");
    }
  }

  ...
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;อันนี้เป็นส่วนหลักเลยนะครับ อันนี้สังเกตนะครับ LineLoginCallback สืบทอดมาจาก AndroidJavaProxy โดยใน constructor เนี่ยเอามาจากชื่อ Class ของใน Java ที่เราประกาศ interface ไว้นะครับ&lt;/p&gt;

&lt;p&gt;ส่วนตอน function login เนี่ยก็จะประมาณนี้ครับ&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_sD-tj5v--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1009/1%2AsbZAm7W0MIkK6SXmi7_2dA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_sD-tj5v--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1009/1%2AsbZAm7W0MIkK6SXmi7_2dA.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;สุดท้ายยยนะครับ อย่าลืมใส่ AndroidManifest.xml ใน Unity3d ด้วยนะครับ​ โดยมันจะมี Template อยู่นะครับ แต่เดี๋ยวผมเอาแต่ส่วนสำคัญที่จำเป็นต้องเพิ่มมาให้ดูละกันครับ&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...

&amp;lt;activity
  android:name="app.zosimos.linesdk.LineSDKActivity"
  android:theme="@style/Theme.AppCompat.NoActionBar"
/&amp;gt;

...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;อันนี้คือการที่เราเพิ่ม Activity ที่เราเขียนใน Java เพิ่มเข้าไปใน Unity นะครับ ถ้าเกิดเราไม่ใส่อันนี้เข้าไปเนี่ยมันจะหาไม่เจอครับบ&lt;/p&gt;

&lt;p&gt;ก็จะประมาณนี้แหละครับ ต้องขอโทษที พอดีบล๊อกนี้รีบเขียนไปหน่อย แต่ยังไงก็ถ้ามีอะไรก็ทักมาถามกันได้ครับ อาจจะพอให้เป็นแนวทางได้ ขอบคุณครับ&lt;/p&gt;

&lt;p&gt;คุยกันได้ &lt;a href="https://www.facebook.com/%E0%B8%97%E0%B8%B8%E0%B8%81%E0%B8%AD%E0%B8%A2%E0%B9%88%E0%B8%B2%E0%B8%87%E0%B9%80%E0%B8%9B%E0%B9%87%E0%B8%99%E0%B9%80%E0%B8%A3%E0%B8%B7%E0%B9%88%E0%B8%AD%E0%B8%87%E0%B8%87%E0%B9%88%E0%B8%B2%E0%B8%A2%E0%B8%96%E0%B9%89%E0%B8%B2%E0%B8%84%E0%B8%B8%E0%B8%93%E0%B8%95%E0%B8%B1%E0%B9%89%E0%B8%87%E0%B9%83%E0%B8%88%E0%B8%AD%E0%B9%88%E0%B8%B2%E0%B8%99-194825381157188/"&gt;ที่นี่&lt;/a&gt; ปล. page นี้ผมเคยเปิดทิ้ง ๆ ไว้ เอาไว้เป็นที่คุยละกันครับ 555&lt;/p&gt;

</description>
      <category>unity</category>
      <category>gamedev</category>
    </item>
    <item>
      <title>วิธีใช้ ECDSA ในการเข้ารหัสและถอดรหัสบน Ethereum Smart contract เพื่อใช้เป็น Source ในการ Random</title>
      <dc:creator>armariya</dc:creator>
      <pubDate>Wed, 20 Jun 2018 19:06:10 +0000</pubDate>
      <link>https://dev.to/armariya/-ecdsa--ethereum-smart-contract--source--random-25mp</link>
      <guid>https://dev.to/armariya/-ecdsa--ethereum-smart-contract--source--random-25mp</guid>
      <description>&lt;p&gt;สวัสดีครับ วันนี้กลับมากลับซีรีส์ที่เขียนเกี่ยวกับ Ethereum นะครับผม สำหรับวันนี้คือเป็นสิ่งที่ผมเพิ่งเรียนรู้มาจากการทำงานนะครับ เรื่องนั้นก็คือ การใช้ ECDSA ในการ verify ตัวตนและใช้เป็น source ในการ random โดยที่ miner จะไม่สามารถรู้ค่าได้ก่อนนะครับ เราไปลุยกันเลย!&lt;/p&gt;

&lt;h4&gt;
  
  
  ECDSA คืออะไร?
&lt;/h4&gt;

&lt;p&gt;เรามาเริ่มที่ตรงนี้กันก่อนครับ 555 ถ้าเรายังไม่รู้จักวิธีที่เราจะใช้ซะก่อน เราจะเรียนไปทำไมถูกมั้ยครับ :) สำหรับ ECDSA นะครับย่อมาจาก Elliptic Curve Digital Signature Algorithm เป็นอัลกอริทึมที่ Bitcoin กับ Ethereum ใช้ในการสร้างตัว Private Key, Public Key และ Address ที่เรา ๆ ใช้กันอยู่นั่นเองครับ นั่นเรื่องของรายละเอียดว่าทำงานยังไง อะไรยังไงนี่ เดี๋ยวไว้บทความถัด ๆ ไปนะครับเนื่องจากไม่งั้นจะยาวมาก (อาจจะไม่มีด้วย 555)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zCF1W6WN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/500/1%2AY89MBlUMqXQ1JwFkPNpPFQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zCF1W6WN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/500/1%2AY89MBlUMqXQ1JwFkPNpPFQ.png" alt="" width="500" height="556"&gt;&lt;/a&gt;รูปตัวอย่างที่ใช้สำหรับในการคำนวณ Eliptic Curve&lt;/p&gt;

&lt;p&gt;ซึ่งตัว ECDSA เป็นอัลกอริทึมสำหรับการสร้าง Private Key และ Public Key โดยการทำงานคร่าว ๆ คือเราสามารถเข้ารหัสด้วย Public Key หรือ Private Key ของเราได้ แต่จะสามารถถอดได้ด้วยคู่ Key ของตัวเองเท่านั้น เช่นถ้าเข้ารหัสด้วย Public Key ก็จะถอดด้วย Private Key ได้เท่านั้น หรือถ้าเข้ารหัสด้วย Private Key ก็จะถอดได้ด้วย Public Key ที่คู่กันเท่านั้น&lt;/p&gt;

&lt;h4&gt;
  
  
  เข้าเรื่อง !!
&lt;/h4&gt;

&lt;p&gt;สำหรับสิ่งต่าง ๆ ที่จะใช้ในบทความนี้ จะมีดังนี้ครับ&lt;/p&gt;

&lt;h4&gt;
  
  
  Requirement
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/trufflesuite/ganache-cli"&gt;ganache-cli&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://truffleframework.com"&gt;Truffle framework&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nodejs.org/en/"&gt;Node.js / npm&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cryptozombies.io"&gt;ความรู้ด้าน Ethereum และ solidity เบื้องต้น&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;ความรู้ภาษา javascript เบื้องต้น&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ถ้าใครมีหมดทั้งสามอย่างแล้วก็มาลุยกันเลยครับบ หรือถ้ายังไม่มีจะอ่านไปก่อนก็ได้ครับ แล้วค่อยมาทำตามทีหลังง ถ้าใครยังไม่ลงก็เข้าตามลิ้งได้เลยนะครับ ผมใส่ไว้ให้ตรง Requirement แล้วครับผม (​สำหรับบทความนี้จะเป็นบน macOS นะครับ สำหรับชาว Windows ต้องขออภัยมา ณ​ ที่นี้&lt;/p&gt;

&lt;p&gt;มาเริ่มกันเลยครับ เริ่มจากเราจะทำการสร้าง Truffle Project ก่อนนะครับ ซึ่ง Truffle ก็คือ framework ในการทำโปรเจค Ethereum นะครับ​ ซึ่งจะช่วยให้เราสามารถ compile, test, deploy ได้ง่ายขึ้นมาก ๆ เริ่มจากทำการสร้าง folder ชื่อว่า ecdsa_test จากนั้นใช้ terminal แล้วเข้าไปที่ folder นั้น ๆ แล้วทำการ Init truffle project ด้วยคำสั่ง&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;truffle init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;จะได้ผลลัพธ์ออกมาแบบนี้นะครับ&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;■ ecdsa_test $ truffle init
Downloading…
Unpacking…
Setting up…
Unbox successful. Sweet!

Commands:
Compile: truffle compile
Migrate: truffle migrate
Test contracts: truffle test
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;จะเห็นว่ามีคำสั่งที่เราสามารถใช้ได้สามอันคือ&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;truffle compile&lt;/strong&gt; ใช้สำหรับในการ compile contract เพื่อเตรียมพร้อมสำหรับการ Deploy&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;truffle migrate/deploy&lt;/strong&gt; ใช้สำหรับการ deploy ขึ้นไปยัง network ของ Ethereum ซึ่งสำหรับในบทความนี้จะ deploy ขึ้น ไปบน private testnet ของเราเองนะครับ&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;truffle test&lt;/strong&gt; จะรัน test ทั้งหมดใน folder test โดยสามารถเขียนด้วยภาษา javascript หรือ solidity ก็ได้ครับ&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;เดี๋ยวเราจะเริ่มเขียนที่ฝั่ง contract ก่อนนะครับ สร้างไฟล์ใน folder ที่ชื่อว่า contracts โดยมีชื่อไฟล์ว่า &lt;strong&gt;SimpleECDSA.sol&lt;/strong&gt; นะครับ มี code ตามนี้&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pragma solidity 0.4.24;

contract SimpleECDSA {
  address private publicKey = 0x831412;

  modifier mustSignWithECDSA(bytes32 hash, uint8 v, bytes32 r, bytes32 s) {
    require(ecrecover(hash, v, r, s) == publicKey);
    _;
  }

  function callWithECDSA(bytes32 hash, uint8 v, bytes32 r, bytes32 s) 
    public 
    view 
    mustSignWithECDSA(hash, v, r, s) 
    returns (uint8) 
  {
    return 1;
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;โอเคเดี๋ยวเราจะมาดูทีละบรรทัดกันครับว่าแต่ละอันมันทำอะไร&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pragma solidity 0.4.24
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;เป็นการบอกว่า file นี้จะใช้ solidity เวอร์ชันอะไร ซึ่งในที่นี้ใช้ 0.4.24 ครับ&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;contract SimpleECDSA {
  address publicKey = 0x831412;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;เป็นการประกาศว่าชื่อ contract คืออะไร และประกาศตัวแปรชื่อ publicKey เป็นประเภท address มีค่าเท่ากับ 0x83142&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;modifier mustSignWithECDSA(
  bytes32 hash, 
  uint8 v, 
  bytes32 r, 
  bytes32 s) 
{
  require(ecrecover(hash, v, r, s) == publicKey);
  _;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;เป็นการประกาศ modifier ที่ชื่อว่า mustSignWithECDSA และใช้ function ecrecover สำหรับถอดรหัสว่า signature ที่ส่งมาสามารถถอดมาได้ตรงกับ public key ของเราหรือไม่&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function callWithECDSA(bytes32 hash, uint8 v, bytes32 r, bytes32 s)
  public
  view
  mustSignWithECDSA(hash, v, r, s)
  returns (uint8)
{
  return 1;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;เป็น function ที่เอาไว้เทสตัว modifier ที่เราสร้างขึ้นเมื่อกี้ ถ้าเกิดว่าเรา verify ผ่านก็จะ return ค่า 1 กลับไป&lt;/p&gt;

&lt;p&gt;เย่ ที่นี่เราก็เตรียมฝั่ง contract เสร็จเรียบร้อยแล้วนะครับ เดี๋ยวเราจะไปเขียนไฟล์ test กัน เอาไว้ใช้สำหรับลอง sign ด้วย private key จากทางฝั่ง javascript แล้วมาถอดที่ฝั่ง contract นะครับ&lt;/p&gt;

&lt;p&gt;แต่ก่อนอื่นเราต้องสร้าง file ที่เอาไว้สำหรับ deploy ก่อนนะครับ สร้าง file ใน folder migration ชื่อว่า &lt;strong&gt;2_deploy_contracts.js&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// 2_deploy_contracts.js
const SimpleECDSA = artifacts.require('./SimpleECDSA.sol');

module.exports = function(deployer) {
  deployer.deploy(SimpleECDSA);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;สำหรับ file นี้ขอไม่อธิบายนะครับบ เป็นส่วน deploy ของ truffle framework จะขอข้ามไปนะครับ เพราะเริ่มยาวแล้ว 555&lt;/p&gt;

&lt;p&gt;ต่อไปเราต้องทำการลง package เพิ่มกันก่อนสำหรับใช้ generate key ใน file test นะนะครับ ใช้คำสั่ง&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nom init -y
npm install --save secp256k1
npm install --save web3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;โอเคครับ ถ้าลงเสร็จแล้วเรามาเริ่มเขียน file test กันเลยครับ ทำการสร้างไฟล์ที่ชื่อว่า &lt;strong&gt;simpleecdsa.js&lt;/strong&gt; แล้วเขียน code ตามนี้ครับ&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// simpleecdsa.js
const SimpleECDSA = artifacts.require('./SimpleECDSA.sol');
const { randomBytes } = require('crypto');
const secp256k1 = require('secp256k1');

contract('SimpleECDSA', async (accounts) =&amp;gt; {
  let simpleECDSAInstance;

  before('setup contract instance', async () =&amp;gt; {
    simpleECDSAInstance = await SimpleECDSA.new();
  });

  it('generate key', async () =&amp;gt; {
    const Web3 = require('web3');
    const web3 = new Web3();
    const messageBuffer = new randomBytes(32);
    const messageHex = messageBuffer.toString('hex');
    let privateKey;
    do {
      privateKey = randomBytes(32);
    } while (!secp256k1.privateKeyVerify(privateKey));

    const privateKeyHex = `0x${privateKey.toString('hex')}`;
    const generatedAccount = web3.eth.accounts.privateKeyToAccount(privateKeyHex);
    console.log(generatedAccount);
  });
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;สำหรับ test ตัวนี้เราจะเอาไว้สำหรับ generate key ขึ้นมาก่อนนะครับ จากนั้นค่อยมาเขียนตัว file test ที่แท้จริงกันครับ 555&lt;/p&gt;

&lt;p&gt;เราถึงเวลาที่จะมา deploy กันแล้วครับ ตอนนี้เราจะต้องทำการเปิด private testnet ของตัวเองขึ้นมาซะก่อน มาถึงตรงนี้ถ้าใครยังไม่ได้ลง ganache-cli ลงซะตอนนี้เลยครับ ด้วยคำสั่ง&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install -g ganache-cli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;พอลงเสร็จแล้วนะครับให้เปิด tab ใหม่บน terminal แล้วทำการรันคำสั่ง ganache-cli&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ganache-cli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ก็เรียบร้อยกันไปตอนนี้เราจะ private testnet อยู่บนเครื่องคอมพิวเตอร์ของเราแล้วนะครับที่ &lt;a href="http://localhost:8545"&gt;http://localhost:8545&lt;/a&gt; นะครับ จากนั้นเราต้องทำการกำหนดค่าของ network ว่าจะให้ truffle deploy contract ของเราลงที่ไหนนะครับ เปิด file &lt;strong&gt;truffle.js&lt;/strong&gt; ที่อยู่ใน project ขึ้นมาแล้วใส่ตามนี้ครับ&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// truffle.js
module.exports = {
  networks: {
    development: {
      host: "127.0.0.1",
      port: 8545,
      network_id: "*"
    }
  }
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;จากนั้นก็ทำการ compile แล้ว deploy ขึ้น private testnet ของเรากันครับ&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;truffle compile
truffle migrate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;การ compile ตัว truffle จะสร้าง folder ที่ชื่อ builds มาให้แล้วข้างในจะมี file ที่ compile เสร็จแล้วอยู่ในนั้น แล้วจากนั้นก็ทำการ migrate ขึ้น testnet ครับ ถ้าไม่มีอะไรผิดพลาด ก็จะได้แบบนี้ครับ จะเห็นว่ามีการเขียนว่า SimpleECDSA Saving successful migration to network…&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--q9GxWaeY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/762/1%2A38XbuQ-M7roNJyrdLd3THw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--q9GxWaeY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/762/1%2A38XbuQ-M7roNJyrdLd3THw.png" alt="" width="762" height="561"&gt;&lt;/a&gt;รูปตอนที่ deploy สำเร็จ&lt;/p&gt;

&lt;p&gt;ถ้าลองกลับไปดูที่ tab ของ ganache-cli จะเห็นว่ามี transaction ในการเรียกและ deploy contract ขึ้นมาครับ ถ้าไม่มี error ก็ฮูเร่ deploy สำเร็จแล้วครับ จากนั้นก็ลองรันเทสกันเลยครับ ด้วยคำสั่ง&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;truffle test
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TtbB3TQ5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/762/1%2AURUDZrgwkdkmAXSWaDoKiw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TtbB3TQ5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/762/1%2AURUDZrgwkdkmAXSWaDoKiw.png" alt="" width="762" height="561"&gt;&lt;/a&gt;รูปภาพของตัว account ที่ generate ขึ้นมาสำเร็จ&lt;/p&gt;

&lt;p&gt;ถ้าเรา run test สำเร็จ เราจะได้ภาพข้างบนนี้ครับ จะเห็นว่าเรามี address, privateKey แล้วนะครับ ทีนี้เราจะเอาไปสองตัวนี้ไปใช้กันครับ โดยเราจะเก็บตัว address ไว้ใน contract ไว้ใช้สำหรับ verify กับ ecrecover ส่วน privateKey เราจะเก็บไว้ที่ test ไว้ใช้สำหรับ sign message นะครับ&lt;/p&gt;

&lt;p&gt;ก่อนอื่นเราก็เอาไป address ไปเก็บที่ contract ก่อนเลยครับ โดยเอาไปแทนที่ตัวแปรที่ชื่อ publicKey ให้เป็นแบบนี้ครับ&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;address private publicKey = 0xACdbA4985df6b4A0C8AdD5DBCfe8360910E5E8b5;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ส่วนใน file simpleecdsa.js จะทำการเพิ่มตัวแปร privateKey และแก้ให้กลายเป็น file test ที่แท้จริง โดยการเอา function generate ออกและเปลี่ยนเป็นการ sign message แทนนะครับ&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// simpleecdsa.js
const SimpleECDSA = artifacts.require('./SimpleECDSA.sol');
const { randomBytes } = require('crypto');

contract('SimpleECDSA', async (accounts) =&amp;gt; {
  let simpleECDSAInstance;
  let privateKey = '0x29a50358bd197f58314159af7e50718ff69c7181efa6f5c1b19ef78171082ef5';
  let web3;
  let generatedAccount;

  before('setup contract instance', async () =&amp;gt; {
    simpleECDSAInstance = await SimpleECDSA.new();
    const Web3 = require('web3');
    web3 = new Web3(Web3.givenProvider || 'http://localhost:8545');
    generatedAccount = web3.eth.accounts.privateKeyToAccount(privateKey);  
});

  it('test sign randomMessage with ecdsa to generate signature use as a random source', async () =&amp;gt; {
    const messageBuffer = randomBytes(32);
    const messageHex = messageBuffer.toString('hex');
    const signatureObject = generatedAccount.sign(messageHex);

    const result = await simpleECDSAInstance.callWithECDSA(
      signatureObject.messageHash,
      signatureObject.v,
      signatureObject.r,
      signatureObject.s
    );

    assert.equal(result, 1);
  });
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;จากนั้นก็ทำการ compile, deploy, test เหมือนเดิมเลยครับ มีเพียงอย่างเดียวที่ต้องเปลี่ยนคือ&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;truffle deploy --reset
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ที่ต้องมี reset เพราะว่าไม่งั้น truffle จะจำไว้แล้วครับว่า SimpleECDSA เคย deploy ไปแล้ว truffle จะ deploy contract อันใหม่ขึ้นไปครับ พอ test เสร็จก็จะเจอความเขียวอันสวยงาม&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vlAVScwo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/762/1%2A9QrZwZwOLtxZ7sPBjT2-jw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vlAVScwo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/762/1%2A9QrZwZwOLtxZ7sPBjT2-jw.png" alt="" width="762" height="561"&gt;&lt;/a&gt;รูปภาพตอน test สำเร็จ&lt;/p&gt;

&lt;p&gt;สิ่งที่เกิดขึ้นก็คือใน contract ผมได้ทำการเอา address ไปเก็บไว้ก่อน เพื่อใช้เป็นตัว verify ส่วนใน file test ผมเอา private key ไปเก็บไว้สำหรับใช้ sign random message พอ sign เสร็จด้วยคำสั่ง &lt;strong&gt;generatedAccount.sign(privateKey)&lt;/strong&gt; ปุ๊บจะได้เป็น signature object แล้วก็ทำการส่งไปซึ่งจะเห็นว่ามี messageHash, v, r, s ซึ่งใน ECDSA เนี่ย (r, s) ถือว่าเป็น signature ครับผม หลังจากที่ส่งไปฝั่ง Ethereum แล้วก็ทำการใช้คำสั่ง&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ecrecover(hash, v, r, s)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;สำหรับการถอดรหัสออกมาว่าได้ Address ตรงกับที่เราประกาศไว้ใน contract รึเปล่านั่นเอง ซึ่งจากตรงนี้เราสามารถนำ r, s เนี่ยไปใช้เป็น random source ได้เลย ตัวอย่างเช่น&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;uint256 randomNumber = uint256(s) % 10000;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;แบบนี้เป็นต้น เย่ จบแล้ว!&lt;/p&gt;

&lt;p&gt;อ๊ะลืมบอกไปนี่คือตัวอย่างหน้าตาของ r กับ s ครับ&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;r: 0x79650c78c66d36e4c1c7edde2c8d45e2db1f19710bbb596d3d60f043b6e87a0c
s: 0x309482307f62e1ca9cdf6017b5a911b2e99cf0443e78a286b7c81694bca0f33b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;เขียนไปเขียนมาเริ่มงงเอง ว่าตกลงเราได้อะไรจากบทความนี้กันแน่ 555 งั้นเดี๋ยวจะสรุปอีกทีละกันครับคือ&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ได้วิธีการ generate key pair ด้วย ECDSA&lt;/li&gt;
&lt;li&gt;ได้รู้การเข้า sign message ใน javascript และนำไปเช็คกับตัว Smart Contract ซึ่งช่วยสามารถให้ Verify ที่มาของคนแรกได้ว่าเป็นคนที่เรา approve รึเปล่า (เช่น ถ้าทำเกม casino งี้ เราก็ไม่อยากให้เขาโกง แต่เล่นผ่านเฉพาะหน้าเว็บเราใช่มั้ยครับ :))&lt;/li&gt;
&lt;li&gt;ได้ random source ที่ miner ไม่สามารถเดาก่อนได้ (ไม่แน่ใจ แต่คิดว่าไม่ได้แล้วนะ ถ้ายังมีอีกช่วยบอกกันด้วยนะครับ 55)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  ข้อเสีย
&lt;/h4&gt;

&lt;p&gt;จากเมื่อกี้เหมือนจะดูดีนะ แต่ว่าข้อเสียของวิธีนี้เลยก็คือ (เขาเรียกวิธีนี้กัน signidice นะ search ดูได้) ด้วยความที่เรา randomMessage ใช่มั้ยครับ ฝั่งคนทำเว็บ เนี่ยสามารถแอบโกงได้ โดยการ randomMessage ไปเรื่อย ๆ จนกว่าจะได้เลขที่ทำให้คนเล่นแพ้ เช่น ถ้าเกมคือทอยลูกเต๋า ถ้าได้ &amp;gt; 3 ชนะ &amp;lt; 3 แพ้ ถ้าเจ้าของเกมจะโกง ก็แค่ randomMessage รัว ๆ จนกว่าจะได้ค่าที่ทำให้ signature ออกมา นำไป random แล้วมีค่า &amp;lt; 3 เท่านั้นเอง&lt;/p&gt;

&lt;p&gt;สำหรับ code ที่เสร็จแล้ว สามารถดูได้ที่ &lt;a href="https://github.com/qapquiz/ecdsa_test"&gt;https://github.com/qapquiz/ecdsa_test&lt;/a&gt; นี่เลยนะครับ&lt;/p&gt;

</description>
      <category>dapps</category>
      <category>ethereum</category>
    </item>
    <item>
      <title>Coinbase กำลังจะเปิดตัว Toshi wallet / Web3 สำหรับ Chrome!</title>
      <dc:creator>armariya</dc:creator>
      <pubDate>Mon, 28 May 2018 14:18:56 +0000</pubDate>
      <link>https://dev.to/armariya/coinbase--toshi-wallet--web3--chrome-18jk</link>
      <guid>https://dev.to/armariya/coinbase--toshi-wallet--web3--chrome-18jk</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F64rb7k6qyyjbi2dhhvcb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F64rb7k6qyyjbi2dhhvcb.png" alt="toshi x chrome image"&gt;&lt;/a&gt;&lt;br&gt;
จากงาน ETH Buenos Aires ทาง Toshi ได้ทำการเปิดภาพให้ดูเป็นตัวอย่างสำหรับตัว Toshi wallet / Web3 บน Chrome ตาม Twitter ด้านล่างนี้&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1000091890396618752-783" src="https://platform.twitter.com/embed/Tweet.html?id=1000091890396618752"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1000091890396618752-783');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1000091890396618752&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;โดยปกติแล้วโลกของการใช้ Ethereum บน Browser นั้น ตอนนี้มีเพียงเจ้าเดียวที่ทำอยู่คือ MetaMask โดยส่วนตัวแล้วผมคิดว่าตัว MetaMask เนี่ยค่อนข้างจะพัฒนาช้า บวกกับ ตัวเวอร์ชัน 1.0.0 ไม่หลุดจากสถานะ Beta สักที ซึ่งก็เข้าใจว่าเป็นเพราะมันเป็นเรื่องเกี่ยวกับเงินด้วยต้องรอบคอบนิดนึง แต่ก็ยังรู้สึกว่ามันช้าไปอยู่ดี การที่ Toshi ได้ออกมาทำอันนี้ถือว่า ดีมาก ๆ อย่างน้อยจะได้มีการแข่งขัน แล้วทำให้การพัฒนามันเป็นไปได้อย่างรวดเร็วว เย่&lt;/p&gt;

&lt;p&gt;บอกตามตรงว่าอยากลองแล้วววว เริ่มเนือยกับ MetaMask อยากลองของใหม่บ้าง ฮ่า ๆๆ&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;แหล่งอ้างอิง&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Toshi : &lt;a href="https://www.toshi.org/" rel="noopener noreferrer"&gt;https://www.toshi.org/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;MetaMask : &lt;a href="https://metamask.io/" rel="noopener noreferrer"&gt;https://metamask.io/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ethereum</category>
      <category>web3</category>
    </item>
  </channel>
</rss>
