tag:blogger.com,1999:blog-63127207544392618132024-03-09T06:46:56.239+08:00工程師的筆記本工程師是個很難失業的職業,因為它會不斷地創造需求,發明非人性的科技,讓平常人永遠需要工程師!!kigihttp://www.blogger.com/profile/09881273562560954871noreply@blogger.comBlogger73125tag:blogger.com,1999:blog-6312720754439261813.post-53823946834099797672020-02-04T09:48:00.001+08:002020-02-04T09:48:14.943+08:00Go 自學筆記<br />
<br />
<h1 style="background-color: white; border-bottom: 1px solid rgb(234, 236, 239); box-sizing: border-box; color: #24292e; font-family: -apple-system, system-ui, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; line-height: 1.25; margin-bottom: 16px; margin-left: 0px; margin-right: 0px; margin-top: 0px !important; padding-bottom: 0.3em;">
<a href="https://github.com/kigichang/go_course">Go 自學筆記</a></h1>
<ul style="background-color: white; box-sizing: border-box; color: #24292e; font-family: -apple-system, system-ui, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; font-size: 16px; margin-bottom: 16px; margin-top: 0px; padding-left: 2em;">
<li style="box-sizing: border-box;">GO 版本: Go 1.13</li>
<li style="box-sizing: border-box; margin-top: 0.25em;">開發環境: Mac OS</li>
<li style="box-sizing: border-box; margin-top: 0.25em;"><a href="https://github.com/kigichang/go_course" style="background-color: initial; box-sizing: border-box; color: #0366d6; text-decoration-line: none;">Source on Github</a></li>
<li style="box-sizing: border-box; margin-top: 0.25em;">文件使用 Markdown 撰寫,建議使用 <a href="https://github.com/shd101wyy/markdown-preview-enhanced" style="background-color: initial; box-sizing: border-box; color: #0366d6; text-decoration-line: none;">Markdown Preview Enhanced</a> 閱讀</li>
</ul>
<div style="background-color: white; box-sizing: border-box; color: #24292e; font-family: -apple-system, system-ui, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; font-size: 16px; margin-bottom: 16px;">
資料主要來自:</div>
<ol style="background-color: white; box-sizing: border-box; color: #24292e; font-family: -apple-system, system-ui, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; font-size: 16px; margin-bottom: 16px; margin-top: 0px; padding-left: 2em;">
<li style="box-sizing: border-box;">官網:<a href="https://golang.org/" rel="nofollow" style="background-color: initial; box-sizing: border-box; color: #0366d6; text-decoration-line: none;">https://golang.org/</a></li>
<li style="box-sizing: border-box; margin-top: 0.25em;">線上學習:<a href="https://tour.golang.org/welcome/1" rel="nofollow" style="background-color: initial; box-sizing: border-box; color: #0366d6; text-decoration-line: none;">A tour of Go</a></li>
<li style="box-sizing: border-box; margin-top: 0.25em;"><a href="https://www.amazon.com/Programming-Language-Addison-Wesley-Professional-Computing-ebook/dp/B0184N7WWS" rel="nofollow" style="background-color: initial; box-sizing: border-box; color: #0366d6; text-decoration-line: none;">The Go Programming Language</a></li>
<li style="box-sizing: border-box; margin-top: 0.25em;"><a href="https://www.manning.com/books/go-web-programming" rel="nofollow" style="background-color: initial; box-sizing: border-box; color: #0366d6; text-decoration-line: none;">Go Web Programming</a></li>
<li style="box-sizing: border-box; margin-top: 0.25em;"><a href="https://www.packtpub.com/networking-and-servers/go-systems-programming" rel="nofollow" style="background-color: initial; box-sizing: border-box; color: #0366d6; text-decoration-line: none;">Go System Programming</a></li>
<li style="box-sizing: border-box; margin-top: 0.25em;"><a href="https://github.com/golang/go/wiki/Mobile" style="background-color: initial; box-sizing: border-box; color: #0366d6; text-decoration-line: none;">Go Mobile</a></li>
<li style="box-sizing: border-box; margin-top: 0.25em;"><a href="https://github.com/golang/go/wiki/WebAssembly" style="background-color: initial; box-sizing: border-box; color: #0366d6; text-decoration-line: none;">Go WebAssembly</a></li>
</ol>
<div style="background-color: white; box-sizing: border-box; color: #24292e; font-family: -apple-system, system-ui, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; font-size: 16px; margin-bottom: 16px;">
其他資源:</div>
<ol style="background-color: white; box-sizing: border-box; color: #24292e; font-family: -apple-system, system-ui, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; font-size: 16px; margin-bottom: 16px; margin-top: 0px; padding-left: 2em;">
<li style="box-sizing: border-box;"><a href="https://go.dev/" rel="nofollow" style="background-color: initial; box-sizing: border-box; color: #0366d6; text-decoration-line: none;">Go Dev</a></li>
<li style="box-sizing: border-box; margin-top: 0.25em;"><a href="https://github.com/golang/go/wiki" style="background-color: initial; box-sizing: border-box; color: #0366d6; text-decoration-line: none;">Go Wiki</a></li>
<li style="box-sizing: border-box; margin-top: 0.25em;"><a href="https://awesome-go.com/" rel="nofollow" style="background-color: initial; box-sizing: border-box; color: #0366d6; text-decoration-line: none;">Awesome Go</a></li>
</ol>
<h2 style="background-color: white; border-bottom: 1px solid rgb(234, 236, 239); box-sizing: border-box; color: #24292e; font-family: -apple-system, system-ui, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em;">
<a aria-hidden="true" class="anchor" href="https://github.com/kigichang/go_course#summary" id="user-content-summary" style="background-color: initial; box-sizing: border-box; color: #0366d6; float: left; line-height: 1; margin-left: -20px; padding-right: 4px; text-decoration-line: none;"><svg aria-hidden="true" class="octicon octicon-link" height="16" version="1.1" viewbox="0 0 16 16" width="16"><path d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z" fill-rule="evenodd"></path></svg></a>Summary</h2>
<ul style="background-color: white; box-sizing: border-box; color: #24292e; font-family: -apple-system, system-ui, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; font-size: 16px; margin-bottom: 16px; margin-top: 0px; padding-left: 2em;">
<li style="box-sizing: border-box;"><a href="https://github.com/kigichang/go_course/blob/master/README.md" style="background-color: initial; box-sizing: border-box; color: #0366d6; text-decoration-line: none;">Summary</a></li>
<li style="box-sizing: border-box; margin-top: 0.25em;"><a href="https://github.com/kigichang/go_course/blob/master/class01" style="background-color: initial; box-sizing: border-box; color: #0366d6; text-decoration-line: none;">01 開發環境與語言簡介</a><ul style="box-sizing: border-box; margin-bottom: 0px; margin-top: 0px; padding-left: 2em;">
<li style="box-sizing: border-box;">參考文件</li>
<li style="box-sizing: border-box; margin-top: 0.25em;">IDE 設定</li>
<li style="box-sizing: border-box; margin-top: 0.25em;">與 C/Java/PHP 簡單比較</li>
</ul>
</li>
<li style="box-sizing: border-box; margin-top: 0.25em;"><a href="https://github.com/kigichang/go_course/blob/master/class02" style="background-color: initial; box-sizing: border-box; color: #0366d6; text-decoration-line: none;">02 程式結構與語法</a><ul style="box-sizing: border-box; margin-bottom: 0px; margin-top: 0px; padding-left: 2em;">
<li style="box-sizing: border-box;">關鍵字</li>
<li style="box-sizing: border-box; margin-top: 0.25em;">基本語法</li>
<li style="box-sizing: border-box; margin-top: 0.25em;"><code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, monospace; font-size: 13.6px; margin: 0px; padding: 0.2em 0.4em;">iota</code></li>
<li style="box-sizing: border-box; margin-top: 0.25em;"><code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, monospace; font-size: 13.6px; margin: 0px; padding: 0.2em 0.4em;">type</code></li>
<li style="box-sizing: border-box; margin-top: 0.25em;">指標</li>
<li style="box-sizing: border-box; margin-top: 0.25em;">Package</li>
</ul>
</li>
<li style="box-sizing: border-box; margin-top: 0.25em;"><a href="https://github.com/kigichang/go_course/blob/master/class03" style="background-color: initial; box-sizing: border-box; color: #0366d6; text-decoration-line: none;">03 Data Types - Basic Types</a><ul style="box-sizing: border-box; margin-bottom: 0px; margin-top: 0px; padding-left: 2em;">
<li style="box-sizing: border-box;">Number</li>
<li style="box-sizing: border-box; margin-top: 0.25em;">Boolean</li>
<li style="box-sizing: border-box; margin-top: 0.25em;">String</li>
<li style="box-sizing: border-box; margin-top: 0.25em;">Zero Value</li>
</ul>
</li>
<li style="box-sizing: border-box; margin-top: 0.25em;"><a href="https://github.com/kigichang/go_course/blob/master/class04" style="background-color: initial; box-sizing: border-box; color: #0366d6; text-decoration-line: none;">04 Data Types - Aggregate Types</a><ul style="box-sizing: border-box; margin-bottom: 0px; margin-top: 0px; padding-left: 2em;">
<li style="box-sizing: border-box;">Array</li>
<li style="box-sizing: border-box; margin-top: 0.25em;">Struct</li>
<li style="box-sizing: border-box; margin-top: 0.25em;">JSON</li>
</ul>
</li>
<li style="box-sizing: border-box; margin-top: 0.25em;"><a href="https://github.com/kigichang/go_course/blob/master/class05" style="background-color: initial; box-sizing: border-box; color: #0366d6; text-decoration-line: none;">05 Data Types - Reference Types</a><ul style="box-sizing: border-box; margin-bottom: 0px; margin-top: 0px; padding-left: 2em;">
<li style="box-sizing: border-box;">Slice<ul style="box-sizing: border-box; margin-bottom: 0px; margin-top: 0px; padding-left: 2em;">
<li style="box-sizing: border-box;">Append Slice</li>
</ul>
</li>
<li style="box-sizing: border-box; margin-top: 0.25em;">Map</li>
</ul>
</li>
<li style="box-sizing: border-box; margin-top: 0.25em;"><a href="https://github.com/kigichang/go_course/blob/master/class06" style="background-color: initial; box-sizing: border-box; color: #0366d6; text-decoration-line: none;">06 Functions</a><ul style="box-sizing: border-box; margin-bottom: 0px; margin-top: 0px; padding-left: 2em;">
<li style="box-sizing: border-box;">語法</li>
<li style="box-sizing: border-box; margin-top: 0.25em;">Pass By Value in Value and Reference Types.</li>
<li style="box-sizing: border-box; margin-top: 0.25em;">First Class</li>
</ul>
</li>
<li style="box-sizing: border-box; margin-top: 0.25em;"><a href="https://github.com/kigichang/go_course/blob/master/class07" style="background-color: initial; box-sizing: border-box; color: #0366d6; text-decoration-line: none;">07 Methods</a><ul style="box-sizing: border-box; margin-bottom: 0px; margin-top: 0px; padding-left: 2em;">
<li style="box-sizing: border-box;">Methods in Value and Pointer</li>
</ul>
</li>
<li style="box-sizing: border-box; margin-top: 0.25em;"><a href="https://github.com/kigichang/go_course/blob/master/class_build_dependency" style="background-color: initial; box-sizing: border-box; color: #0366d6; text-decoration-line: none;">Build and Dependency</a><ul style="box-sizing: border-box; margin-bottom: 0px; margin-top: 0px; padding-left: 2em;">
<li style="box-sizing: border-box;">Build cross platform</li>
<li style="box-sizing: border-box; margin-top: 0.25em;">Go Module <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, monospace; font-size: 13.6px; margin: 0px; padding: 0.2em 0.4em;">go mod</code></li>
</ul>
</li>
<li style="box-sizing: border-box; margin-top: 0.25em;"><a href="https://github.com/kigichang/go_course/blob/master/class08" style="background-color: initial; box-sizing: border-box; color: #0366d6; text-decoration-line: none;">08 Interface</a><ul style="box-sizing: border-box; margin-bottom: 0px; margin-top: 0px; padding-left: 2em;">
<li style="box-sizing: border-box;">Interface in Struct and Pointer</li>
<li style="box-sizing: border-box; margin-top: 0.25em;">Interface value</li>
<li style="box-sizing: border-box; margin-top: 0.25em;">Interface puzzle (interface nil problem)</li>
</ul>
</li>
<li style="box-sizing: border-box; margin-top: 0.25em;"><a href="https://github.com/kigichang/go_course/blob/master/class09" style="background-color: initial; box-sizing: border-box; color: #0366d6; text-decoration-line: none;">09 OOP in Go</a><ul style="box-sizing: border-box; margin-bottom: 0px; margin-top: 0px; padding-left: 2em;">
<li style="box-sizing: border-box;">Encapsulation</li>
<li style="box-sizing: border-box; margin-top: 0.25em;">Inheritance (fake)</li>
<li style="box-sizing: border-box; margin-top: 0.25em;">Polymorphism</li>
</ul>
</li>
<li style="box-sizing: border-box; margin-top: 0.25em;"><a href="https://github.com/kigichang/go_course/blob/master/class10" style="background-color: initial; box-sizing: border-box; color: #0366d6; text-decoration-line: none;">10 Defer and Error Handling</a><ul style="box-sizing: border-box; margin-bottom: 0px; margin-top: 0px; padding-left: 2em;">
<li style="box-sizing: border-box;">Defer<ul style="box-sizing: border-box; margin-bottom: 0px; margin-top: 0px; padding-left: 2em;">
<li style="box-sizing: border-box;">Closure Binding</li>
</ul>
</li>
<li style="box-sizing: border-box; margin-top: 0.25em;">Panic and Recover</li>
<li style="box-sizing: border-box; margin-top: 0.25em;">Errors (new feature in Go 1.13)</li>
</ul>
</li>
<li style="box-sizing: border-box; margin-top: 0.25em;"><a href="https://github.com/kigichang/go_course/blob/master/class11" style="background-color: initial; box-sizing: border-box; color: #0366d6; text-decoration-line: none;">11 Concurrency - Goroutine</a><ul style="box-sizing: border-box; margin-bottom: 0px; margin-top: 0px; padding-left: 2em;">
<li style="box-sizing: border-box;">Keyword <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, monospace; font-size: 13.6px; margin: 0px; padding: 0.2em 0.4em;">go</code></li>
<li style="box-sizing: border-box; margin-top: 0.25em;"><code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, monospace; font-size: 13.6px; margin: 0px; padding: 0.2em 0.4em;">sync.WaitGroup</code></li>
</ul>
</li>
<li style="box-sizing: border-box; margin-top: 0.25em;"><a href="https://github.com/kigichang/go_course/blob/master/class12" style="background-color: initial; box-sizing: border-box; color: #0366d6; text-decoration-line: none;">12 Concurrency - Channel</a><ul style="box-sizing: border-box; margin-bottom: 0px; margin-top: 0px; padding-left: 2em;">
<li style="box-sizing: border-box;">Buffered channel</li>
<li style="box-sizing: border-box; margin-top: 0.25em;">Producer and Consumer Pattern</li>
<li style="box-sizing: border-box; margin-top: 0.25em;">Actor Pattern</li>
<li style="box-sizing: border-box; margin-top: 0.25em;"><code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, monospace; font-size: 13.6px; margin: 0px; padding: 0.2em 0.4em;">select</code> to monitor channels</li>
</ul>
</li>
<li style="box-sizing: border-box; margin-top: 0.25em;"><a href="https://github.com/kigichang/go_course/blob/master/class13" style="background-color: initial; box-sizing: border-box; color: #0366d6; text-decoration-line: none;">13 Context</a></li>
<li style="box-sizing: border-box; margin-top: 0.25em;"><a href="https://github.com/kigichang/go_course/blob/master/class14" style="background-color: initial; box-sizing: border-box; color: #0366d6; text-decoration-line: none;">14 Testing</a></li>
<li style="box-sizing: border-box; margin-top: 0.25em;"><a href="https://github.com/kigichang/go_course/blob/master/class15" style="background-color: initial; box-sizing: border-box; color: #0366d6; text-decoration-line: none;">15 flag and spf13 Cobra/Viper</a></li>
<li style="box-sizing: border-box; margin-top: 0.25em;"><a href="https://github.com/kigichang/go_course/blob/master/class16" style="background-color: initial; box-sizing: border-box; color: #0366d6; text-decoration-line: none;">16 MySQL</a></li>
<li style="box-sizing: border-box; margin-top: 0.25em;"><a href="https://github.com/kigichang/go_course/blob/master/class17" style="background-color: initial; box-sizing: border-box; color: #0366d6; text-decoration-line: none;">17 Web (Gorilla web toolkit)</a><ul style="box-sizing: border-box; margin-bottom: 0px; margin-top: 0px; padding-left: 2em;">
<li style="box-sizing: border-box;">Go Template 語法</li>
<li style="box-sizing: border-box; margin-top: 0.25em;">Context in Request (Request-Scoped)</li>
<li style="box-sizing: border-box; margin-top: 0.25em;">Cookie</li>
<li style="box-sizing: border-box; margin-top: 0.25em;">Gorilla<ul style="box-sizing: border-box; margin-bottom: 0px; margin-top: 0px; padding-left: 2em;">
<li style="box-sizing: border-box;">Mux<ul style="box-sizing: border-box; margin-bottom: 0px; margin-top: 0px; padding-left: 2em;">
<li style="box-sizing: border-box;">Middleware</li>
</ul>
</li>
<li style="box-sizing: border-box; margin-top: 0.25em;">Shema</li>
<li style="box-sizing: border-box; margin-top: 0.25em;">Secure Cookie</li>
<li style="box-sizing: border-box; margin-top: 0.25em;">CSRF</li>
</ul>
</li>
</ul>
</li>
<li style="box-sizing: border-box; margin-top: 0.25em;"><a href="https://github.com/kigichang/go_course/blob/master/class18" style="background-color: initial; box-sizing: border-box; color: #0366d6; text-decoration-line: none;">18 RESTful, Protobuf and gRPC</a><ul style="box-sizing: border-box; margin-bottom: 0px; margin-top: 0px; padding-left: 2em;">
<li style="box-sizing: border-box;">RESTful using Gorilla</li>
<li style="box-sizing: border-box; margin-top: 0.25em;">Protobuf<ul style="box-sizing: border-box; margin-bottom: 0px; margin-top: 0px; padding-left: 2em;">
<li style="box-sizing: border-box;">Protoc (Protobuf Compliler)</li>
</ul>
</li>
<li style="box-sizing: border-box; margin-top: 0.25em;">gRPC (Client and Service)</li>
</ul>
</li>
<li style="box-sizing: border-box; margin-top: 0.25em;"><a href="https://github.com/kigichang/go_course/blob/master/class19" style="background-color: initial; box-sizing: border-box; color: #0366d6; text-decoration-line: none;">19 Reflection and Struct Tag</a><ul style="box-sizing: border-box; margin-bottom: 0px; margin-top: 0px; padding-left: 2em;">
<li style="box-sizing: border-box;">Type and Value</li>
<li style="box-sizing: border-box; margin-top: 0.25em;">Strut Tag</li>
<li style="box-sizing: border-box; margin-top: 0.25em;">Check Interface implementation</li>
<li style="box-sizing: border-box; margin-top: 0.25em;">Zero Value</li>
<li style="box-sizing: border-box; margin-top: 0.25em;">Make Slice</li>
<li style="box-sizing: border-box; margin-top: 0.25em;">Make Map</li>
<li style="box-sizing: border-box; margin-top: 0.25em;">Make Function</li>
</ul>
</li>
<li style="box-sizing: border-box; margin-top: 0.25em;"><a href="https://github.com/kigichang/go_course/blob/master/class20" style="background-color: initial; box-sizing: border-box; color: #0366d6; text-decoration-line: none;">20 Cowork with C/C++</a><ul style="box-sizing: border-box; margin-bottom: 0px; margin-top: 0px; padding-left: 2em;">
<li style="box-sizing: border-box;">Go <code style="background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; box-sizing: border-box; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, monospace; font-size: 13.6px; margin: 0px; padding: 0.2em 0.4em;">unsafe</code> Package</li>
<li style="box-sizing: border-box; margin-top: 0.25em;">Go String and *C.char</li>
<li style="box-sizing: border-box; margin-top: 0.25em;">Go call C<ul style="box-sizing: border-box; margin-bottom: 0px; margin-top: 0px; padding-left: 2em;">
<li style="box-sizing: border-box;">Swig</li>
<li style="box-sizing: border-box; margin-top: 0.25em;">DIY</li>
</ul>
</li>
<li style="box-sizing: border-box; margin-top: 0.25em;">C Call Go with Static Library</li>
</ul>
</li>
<li style="box-sizing: border-box; margin-top: 0.25em;"><a href="https://github.com/kigichang/go_course/blob/master/class21" style="background-color: initial; box-sizing: border-box; color: #0366d6; text-decoration-line: none;">21 Go WebAssembly</a><ul style="box-sizing: border-box; margin-bottom: 0px; margin-top: 0px; padding-left: 2em;">
<li style="box-sizing: border-box;">WebAssembly Introduction</li>
<li style="box-sizing: border-box; margin-top: 0.25em;">DOM in Go WASM<ul style="box-sizing: border-box; margin-bottom: 0px; margin-top: 0px; padding-left: 2em;">
<li style="box-sizing: border-box;">Selector</li>
<li style="box-sizing: border-box; margin-top: 0.25em;">Property</li>
<li style="box-sizing: border-box; margin-top: 0.25em;">Method</li>
<li style="box-sizing: border-box; margin-top: 0.25em;">Event</li>
</ul>
</li>
<li style="box-sizing: border-box; margin-top: 0.25em;">Create a Javascript Object</li>
<li style="box-sizing: border-box; margin-top: 0.25em;">Go call Javascript</li>
<li style="box-sizing: border-box; margin-top: 0.25em;">Javascript Call Go</li>
<li style="box-sizing: border-box; margin-top: 0.25em;">File and FileReader</li>
<li style="box-sizing: border-box; margin-top: 0.25em;">Conversion Javascript Uint8Array and Go Byte Slice</li>
</ul>
</li>
</ul>
<h2 style="background-color: white; border-bottom: 1px solid rgb(234, 236, 239); box-sizing: border-box; color: #24292e; font-family: -apple-system, system-ui, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; line-height: 1.25; margin-bottom: 16px; margin-top: 24px; padding-bottom: 0.3em;">
<a aria-hidden="true" class="anchor" href="https://github.com/kigichang/go_course#%E6%96%B0%E5%A2%9E" id="user-content-新增" style="background-color: initial; box-sizing: border-box; color: #0366d6; float: left; line-height: 1; margin-left: -20px; padding-right: 4px; text-decoration-line: none;"><svg aria-hidden="true" class="octicon octicon-link" height="16" version="1.1" viewbox="0 0 16 16" width="16"><path d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z" fill-rule="evenodd"></path></svg></a>新增</h2>
<ul style="background-color: white; box-sizing: border-box; color: #24292e; font-family: -apple-system, system-ui, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; font-size: 16px; margin-bottom: 0px !important; margin-top: 0px; padding-left: 2em;">
<li style="box-sizing: border-box;">Go 1.13 Error 新功能</li>
<li style="box-sizing: border-box; margin-top: 0.25em;">Go WebAssembly</li>
</ul>
kigihttp://www.blogger.com/profile/09881273562560954871noreply@blogger.com1tag:blogger.com,1999:blog-6312720754439261813.post-15268663314685756652017-11-17T16:30:00.000+08:002017-11-17T16:32:06.368+08:00Logarithmic Market Scoring Rule (LMSR) Prediction Market on Hyperledger Fabric邊學 Fabric,Golang 邊實作 LMSR 預測市場模型。
<br />
<br />
Prototype: <a href="https://github.com/kigichang/diviner"> https://github.com/kigichang/diviner</a>kigihttp://www.blogger.com/profile/09881273562560954871noreply@blogger.com1tag:blogger.com,1999:blog-6312720754439261813.post-87018134783633189712015-07-26T22:39:00.000+08:002015-07-26T22:39:01.942+08:00Scala Other (Session 10)<div class='mymarkdown'>
<h1 id="toc_0">Scala Other</h1>
<h2 id="toc_1">Lazy</h2>
<p>Scala 允許資源在要使用時才載入。只要在宣告時,加 <code>lazy</code> 這個關鍵字。</p>
<p>eg: 取得資料庫連線</p>
<pre><code>lazy val conn = DriverManager.getConnection
lazy val stmt = conn.preparedStatement(....)
lazy val rs = stmt.executeQuery()
try {
stmt.setInt(1, xxx)
while (rs.next) {
...
}
}
catch {
case ex: Exception =>
}
finally {
DBUtils.close(rs)
DBUtils.close(stmt)
DBUtils.close(conn)
}</code></pre>
<p>當執行到 <code>stmt.setInt(1, xxx)</code> 時,才會開始初始化 <code>PreparedStatement</code>,在初始化 <code>PreparedStatement</code> 時,才會去初始化 <code>Connection</code>。</p>
<h2 id="toc_2">Implicit</h2>
<h3 id="toc_3">Implicit Parameter</h3>
<p>Implicit 的技術在 Scala 使用很多,以 collection 為例,從 Java Collection 轉換成 Scala Collection 的版本,都是用 implicit 的方式來完成。</p>
<p>在 OOP 最常見的 implicit 是 <code>this</code> 這個關鍵字。在 OOP 的 Class 內,並沒有宣告 <code>this</code> 這個變數,卻可以使用。</p>
<p>Python 的設計哲學是不允許 implicit 的,因此寫 Python 常會宣告 <code>self</code> 來代表 <code>this</code>。</p>
<p>eg:</p>
<pre><code>scala> implicit val a = 10
a: Int = 10
scala> def test(a: Int)(implicit b: Int) = a + b
test: (a: Int)(implicit b: Int)Int
scala> test(1000)
res0: Int = 1010</code></pre>
<p>當宣告的參數是 <code>implicit</code> 時,Scala Compiler 會去找符合條件的 <code>implicit</code> 變數。</p>
<p><code>implicit</code> 的設計會讓系統更加靈活,如 <code>Future</code> 使用的 Thread Pool。Scala 有內建 Thread Pool,但也可以自定 Thread Pool 後,修改 import 的部分後,其他的部分不用再修改。</p>
<p>附註: How <code>this</code> work (c++)</p>
<p>Source code: </p>
<pre><code>class CRect {
private:
int m_color;
public:
void setcolor(int color) {
m_color = color;
}
}</code></pre>
<p>After Compiling:</p>
<pre><code>class CRect {
private:
int m_color;
public:
void setcolor(int color, (CRect*)this) {
this->m_color = color;
}
}</code></pre>
<h3 id="toc_4">Implicit Conversions</h3>
<p>在針對 Java Collection 轉成 Scala 相對應的 Collection,都使用 implicit function 在做轉換。</p>
<p>eg: Scala wrapAsScala</p>
<pre><code>implicit def asScalaBuffer[A](l: ju.List[A]): mutable.Buffer[A] = l match {
case MutableBufferWrapper(wrapped) => wrapped
case _ =>new JListWrapper(l)
}</code></pre>
<h2 id="toc_5">Tail Recursion</h2>
<p>Scala Compiler 會針對 Tail Recursion 做最佳化。所謂的 Tail Recursion 是指,把做 recursion 放在<strong>最後一個指令</strong>。</p>
<p>eg: GCD</p>
<pre><code>def gcd(a: Int, b: Int): Int = if (a == 0) b else gcd(b % a, a)</code></pre>
<p>Recursion 放在最後一行,Scala Compiler 會針對這類型的寫法做最佳化處理。</p>
<p>eg: <strong>非</strong> Tail Recursion,因為最後是 recursive call 後再 <code>+ 1</code>。</p>
<pre><code>def boom(x: Int): Int = if (x == 0) throw new Exception("boom!") else boom(x - 1) + 1</code></pre>
<p>執行的結果:</p>
<pre><code>scala> boom(2)
java.lang.Exception: boom!
at .boom(<console>:7)
at .boom(<console>:7)
at .boom(<console>:7)
... 33 elided</code></pre>
<p>上例中,最後一行是 <code>boom(x - 1) + 1</code>,因此不是 Tail Recursion。執行的結果中,有三個 <code>.boom</code>,代表有三個 stack frame。所以是以傳統 recursive call 在進行。</p>
<p>需改寫成:</p>
<pre><code>scala> def boom(x: Int): Int = if (x == 0) throw new Exception("boom!") else boom(x - 1)
boom: (x: Int)Int</code></pre>
<p>結果:</p>
<pre><code>scala> boom(2)
java.lang.Exception: boom!
at .boom(<console>:8)
... 33 elided</code></pre>
<p>改寫後的結果,只有一個 <code>.boom</code>。Scala Compiler 會把 Tail Recursion 改寫成只用一個 stack frame 來進行。</p>
<p>範例來自:</p>
<p><a href="http://www.amazon.com/Programming-Scala-Comprehensive-Step-Step/dp/0981531644">Programming in Scala: A Comprehensive Step-by-Step Guide, 2nd Edition</a></p>
<script type="text/x-mathjax-config">
if (typeof MathJaxListener !== 'undefined') {
MathJax.Hub.Register.StartupHook('End', function () {
MathJaxListener.invokeCallbackForKey_('End');
});
}
</script>
<script type="text/javascript" src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
</div>kigihttp://www.blogger.com/profile/09881273562560954871noreply@blogger.com0tag:blogger.com,1999:blog-6312720754439261813.post-68094032048012018812015-07-26T22:36:00.003+08:002015-07-26T22:36:56.649+08:00Scala Parallel Computing (Session 9)<div class='mymarkdown'>
<h1 id="toc_0">Scala Parallel Computing</h1>
<p>平行化處理,很適合用在 I/O bound 的程式,讓 I/O 可以同時間被處理,讓 CPU 等待的時間縮到最短。</p>
<h2 id="toc_1">Future and Await</h2>
<p>以往在 Java ,是使用 <code>Thread</code> 來進行計算。Scala 提供 <code>Future</code> 來儲存<strong>尚未</strong>完成的結果,在使用 <code>Future</code> 時,需要 <code>import concurrent.ExecutionContext.Implicits.global</code>,這一行的用意是使用 Scala 內建的 Thread Pool。在使用 <code>Future</code> 時,會需要使用 <code>Thread</code> 來進行計算。</p>
<p>在 Multi-Thread 環境下,通常主程式需要等待所有的 Thread 完成後,才能結束程式。Scala 提供 <code>Await</code> 等待 Thread 執行的結果。</p>
<p>eg: 同時讀取兩個檔案的內容</p>
<pre><code>package com.example
import scala.concurrent.Await
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
import scala.concurrent.TimeoutException
import scala.concurrent.duration.Duration
import scala.io.Source
object FutureTest {
def readFile(file: String): StringBuilder = {
val ret = new StringBuilder
Source.fromFile(file).getLines() foreach { line =>
ret ++= (line + "\r\n")
}
ret
}
def main(args: Array[String]) {
println("start")
val time = System.currentTimeMillis()
val future1 = Future { readFile("ufo_awesome_1.tsv") }
val future2 = Future { readFile("ufo_awesome_2.tsv") }
val result = Await.result(Future.sequence(Seq(future1, future2)), Duration.Inf)
println(s"end and cost: ${System.currentTimeMillis() - time} ms")
}
}</code></pre>
<p>使用 <code>Future</code> 將 <code>readFile</code> 工作包裝起來,在 <code>val future1 = Future { readFile("ufo_awesome_1.tsv") }</code> 會產生一個 <code>Thread</code> 來處理,並且立即執行下一行的工作。最後使用 <code>Await.result</code> 取得結果,或者也可以使用 <code>Await.ready</code>。</p>
<p>如果需要等待多個 <code>Future</code> 的執行結果,先將需要等待的 <code>Future</code>,組成一個 <code>Seq</code>,再使用 <code>Future.sequence</code> 組合後回傳單一個 <code>Future</code>後,再使用 <code>Await</code> 等待執行的結果。</p>
<p>如果移除 <code>val result = Await.result(Future.sequence(Seq(future1, future2)), Duration.Inf)</code> 會發現主程式一下子就執行完畢。</p>
<h3 id="toc_2">Callback</h3>
<h4 id="toc_3">onSuccess</h4>
<p>顧名思義就是當 <code>Future</code> 包裝的工作執行<strong>成功</strong>時,會執行的工作。</p>
<p>eg:</p>
<pre><code>package com.example
import scala.concurrent.Await
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
import scala.concurrent.TimeoutException
import scala.concurrent.duration.Duration
import scala.io.Source
object FutureTest {
def readFile(file: String): StringBuilder = {
val ret = new StringBuilder
Source.fromFile(file).getLines() foreach { line =>
ret ++= (line + "\r\n")
}
ret
}
def main(args: Array[String]) {
println("start")
val time = System.currentTimeMillis()
println(s"${System.currentTimeMillis()} - create future")
val future = Future { readFile("ufo_awesome_1.tsv"); println(s"${System.currentTimeMillis()} - read complete") }
println(s"${System.currentTimeMillis()} - register onSuccess")
future onSuccess {
case sb => println(s"${System.currentTimeMillis()} - success")
}
println(s"${System.currentTimeMillis()} - await")
val result = Await.result(future, Duration.Inf)
println(s"end and cost: ${System.currentTimeMillis() - time} ms")
}
}</code></pre>
<p>結果:</p>
<pre><code>start
1436100618543 - create future
1436100618816 - register onSuccess
1436100618818 - await
1436100620040 - read complete
end and cost: 1502 ms
1436100620042 - success</code></pre>
<p>注意當宣告完 <code>onSuccess</code> 時,主程式並不會 <code>Future</code> 執行結束,而是往下繼續,一直到 <code>Future</code> 的工作完成後,才會執行 <code>onSuccess</code>。</p>
<h4 id="toc_4">onFailure</h4>
<p>當 <code>Future</code> 內的工作,有發生 <code>Exception</code> or <code>Error</code> 時 (也就是有 <code>Throwable</code> )。 <code>onFailure</code> 並不會做 <code>catch</code> 的動作。這一點要特別注意</p>
<p>eg:</p>
<pre><code>package com.example
import scala.concurrent.Await
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
import scala.concurrent.TimeoutException
import scala.concurrent.duration.Duration
import scala.io.Source
import scala.util.control.NonFatal
object FutureTest {
def readFile(file: String): StringBuilder = {
val ret = new StringBuilder
Source.fromFile(file).getLines() foreach { line =>
ret ++= (line + "\r\n")
}
ret
}
def main(args: Array[String]) {
println("start")
val time = System.currentTimeMillis()
println(s"${System.currentTimeMillis()} - create future")
/* ufo_awesome_3.tsv 不存在*/
val future = Future { readFile("ufo_awesome_3.tsv"); println(s"${System.currentTimeMillis()} - read complete") }
println(s"${System.currentTimeMillis()} - register onSuccess")
future onSuccess {
case sb => println(s"${System.currentTimeMillis()} - success")
}
println(s"${System.currentTimeMillis()} - register onFailure")
future onFailure {
case ex: Exception => println(s"${System.currentTimeMillis()} - failure")
}
println(s"${System.currentTimeMillis()} - await")
val result = Await.result(future, Duration.Inf)
println(s"end and cost: ${System.currentTimeMillis() - time} ms")
}
}</code></pre>
<p>結果:</p>
<pre><code>start
1436102289048 - create future
1436102289345 - register onSuccess
1436102289350 - register onFailure
1436102289351 - failure
1436102289352 - await
Exception in thread "main" java.io.FileNotFoundException: ufo_awesome_3.tsv (No such file or directory)
at java.io.FileInputStream.open0(Native Method)
at java.io.FileInputStream.open(FileInputStream.java:195)
at java.io.FileInputStream.<init>(FileInputStream.java:138)
at scala.io.Source$.fromFile(Source.scala:91)
at scala.io.Source$.fromFile(Source.scala:76)
at scala.io.Source$.fromFile(Source.scala:54)
at com.example.FutureTest$.readFile(FutureTest.scala:19)
at com.example.FutureTest$$anonfun$1.apply$mcV$sp(FutureTest.scala:44)
at com.example.FutureTest$$anonfun$1.apply(FutureTest.scala:44)
at com.example.FutureTest$$anonfun$1.apply(FutureTest.scala:44)
at scala.concurrent.impl.Future$PromiseCompletingRunnable.liftedTree1$1(Future.scala:24)
at scala.concurrent.impl.Future$PromiseCompletingRunnable.run(Future.scala:24)
at scala.concurrent.impl.ExecutionContextImpl$AdaptedForkJoinTask.exec(ExecutionContextImpl.scala:121)
at scala.concurrent.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260)
at scala.concurrent.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1339)
at scala.concurrent.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979)
at scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107)</code></pre>
<h4 id="toc_5">onComplete</h4>
<p>當 <code>Future</code> 內的工作執行完畢,不論成功或失敗。</p>
<p>eg:</p>
<pre><code>package com.example
import scala.concurrent.Await
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
import scala.concurrent.TimeoutException
import scala.concurrent.duration.Duration
import scala.io.Source
import scala.util.control.NonFatal
import scala.util.Success
import scala.util.Failure
object FutureTest {
def readFile(file: String): StringBuilder = {
val ret = new StringBuilder
Source.fromFile(file).getLines() foreach { line =>
ret ++= (line + "\r\n")
}
ret
}
def main(args: Array[String]) {
println("start")
val time = System.currentTimeMillis()
println(s"${System.currentTimeMillis()} - create future")
/* ufo_awesome_3.tsv 不存在*/
val future = Future { readFile("ufo_awesome_3.tsv"); println(s"${System.currentTimeMillis()} - read complete") }
println(s"${System.currentTimeMillis()} - register onComplete")
future onComplete {
case Success(sb) => println(s"${System.currentTimeMillis()} - onComplete - success")
case Failure(error) => println(s"${System.currentTimeMillis()} - onComplete - failure ${error.toString()}")
}
println(s"${System.currentTimeMillis()} - await")
val result = Await.result(future, Duration.Inf)
println(s"end and cost: ${System.currentTimeMillis() - time} ms")
}
}</code></pre>
<p>結果:</p>
<pre><code>start
1436105851821 - create future
1436105852172 - register onComplete
1436105852175 - await
1436105852177 - onComplete - failure java.io.FileNotFoundException: ufo_awesome_3.tsv (No such file or directory)
Exception in thread "main" java.io.FileNotFoundException: ufo_awesome_3.tsv (No such file or directory)
at java.io.FileInputStream.open0(Native Method)
at java.io.FileInputStream.open(FileInputStream.java:195)
at java.io.FileInputStream.<init>(FileInputStream.java:138)
at scala.io.Source$.fromFile(Source.scala:91)
at scala.io.Source$.fromFile(Source.scala:76)
at scala.io.Source$.fromFile(Source.scala:54)
at com.example.FutureTest$.readFile(FutureTest.scala:21)
at com.example.FutureTest$$anonfun$1.apply$mcV$sp(FutureTest.scala:46)
at com.example.FutureTest$$anonfun$1.apply(FutureTest.scala:46)
at com.example.FutureTest$$anonfun$1.apply(FutureTest.scala:46)
at scala.concurrent.impl.Future$PromiseCompletingRunnable.liftedTree1$1(Future.scala:24)
at scala.concurrent.impl.Future$PromiseCompletingRunnable.run(Future.scala:24)
at scala.concurrent.impl.ExecutionContextImpl$AdaptedForkJoinTask.exec(ExecutionContextImpl.scala:121)
at scala.concurrent.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260)
at scala.concurrent.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1339)
at scala.concurrent.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979)
at scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107)
</code></pre>
<h4 id="toc_6">多個 Callback</h4>
<p>一個 <code>Future</code> 允許有多個 <code>onSuccess</code>, <code>onFailure</code>, 及 <code>onComplete</code> 。執行的順序不一定會依照程式碼的順序。</p>
<p>eg:</p>
<pre><code>package com.example
import scala.concurrent.Await
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
import scala.concurrent.TimeoutException
import scala.concurrent.duration.Duration
import scala.io.Source
import scala.util.control.NonFatal
import scala.util.Success
import scala.util.Failure
/**
* @author kigi
*/
object FutureTest {
def readFile(file: String): StringBuilder = {
val ret = new StringBuilder
Source.fromFile(file).getLines() foreach { line =>
ret ++= (line + "\r\n")
}
ret
}
def main(args: Array[String]) {
println("start")
val time = System.currentTimeMillis()
/*
val future1 = Future { readFile("ufo_awesome_1.tsv") }
val future2 = Future { readFile("ufo_awesome_2.tsv") }
val result = Await.result(Future.sequence(Seq(future1, future2)), Duration.Inf)
*/
println(s"${System.currentTimeMillis()} - create future")
//val future = Future { readFile("ufo_awesome_1.tsv"); println(s"${System.currentTimeMillis()} - read complete") }
/* ufo_awesome_3.tsv 不存在*/
val future = Future { readFile("ufo_awesome_3.tsv"); println(s"${System.currentTimeMillis()} - read complete") }
println(s"${System.currentTimeMillis()} - register onSuccess")
future onSuccess {
case sb => println(s"${System.currentTimeMillis()} - success")
}
println(s"${System.currentTimeMillis()} - register onFailure")
future onFailure {
case ex: Exception => println(s"${System.currentTimeMillis()} - failure")
}
println(s"${System.currentTimeMillis()} - register onComplete")
future onComplete {
case Success(sb) => println(s"${System.currentTimeMillis()} - onComplete - success")
case Failure(error) => println(s"${System.currentTimeMillis()} - onComplete - failure ${error.toString()}")
}
println(s"${System.currentTimeMillis()} - await")
val result = Await.result(future, Duration.Inf)
println(s"end and cost: ${System.currentTimeMillis() - time} ms")
}
}</code></pre>
<p>結果:</p>
<pre><code>start
1436105970847 - create future
1436105971148 - register onSuccess
1436105971151 - register onFailure
1436105971153 - register onComplete
1436105971154 - failure
1436105971155 - await
1436105971155 - onComplete - failure java.io.FileNotFoundException: ufo_awesome_3.tsv (No such file or directory)
Exception in thread "main" java.io.FileNotFoundException: ufo_awesome_3.tsv (No such file or directory)
at java.io.FileInputStream.open0(Native Method)
at java.io.FileInputStream.open(FileInputStream.java:195)
at java.io.FileInputStream.<init>(FileInputStream.java:138)
at scala.io.Source$.fromFile(Source.scala:91)
at scala.io.Source$.fromFile(Source.scala:76)
at scala.io.Source$.fromFile(Source.scala:54)
at com.example.FutureTest$.readFile(FutureTest.scala:21)
at com.example.FutureTest$$anonfun$1.apply$mcV$sp(FutureTest.scala:46)
at com.example.FutureTest$$anonfun$1.apply(FutureTest.scala:46)
at com.example.FutureTest$$anonfun$1.apply(FutureTest.scala:46)
at scala.concurrent.impl.Future$PromiseCompletingRunnable.liftedTree1$1(Future.scala:24)
at scala.concurrent.impl.Future$PromiseCompletingRunnable.run(Future.scala:24)
at scala.concurrent.impl.ExecutionContextImpl$AdaptedForkJoinTask.exec(ExecutionContextImpl.scala:121)
at scala.concurrent.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260)
at scala.concurrent.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1339)
at scala.concurrent.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979)
at scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107)</code></pre>
<h3 id="toc_7">Map 及 flatMap</h3>
<p><code>Future</code> 也有支援 <code>map</code> 及 <code>flatMap</code>。也就是說可以利用 <code>map</code> 及 <code>flatMap</code> 來進一步做資料處理。</p>
<p>eg: 取出檔案每一行後,計算每一行的長度,最後加總。</p>
<pre><code>package com.example
import scala.concurrent.Await
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
import scala.concurrent.TimeoutException
import scala.concurrent.duration.Duration
import scala.io.Source
import scala.util.control.NonFatal
import scala.util.Success
import scala.util.Failure
object FutureTest {
def readFile(file: String): StringBuilder = {
val ret = new StringBuilder
Source.fromFile(file).getLines() foreach { line =>
ret ++= (line + "\r\n")
}
ret
}
def main(args: Array[String]) {
println("start")
val time = System.currentTimeMillis()
/* Map Start */
val future1 = Future { Source.fromFile("ufo_awesome_1.tsv").getLines().toSeq }
val future2 = future1 map { seq =>
seq.map { _.length }
}
val result = Await.result(future2, Duration.Inf)
println(s"total: ${result.reduce( _ + _)}")
/* Map End */
println(s"end and cost: ${System.currentTimeMillis() - time} ms")
}
}</code></pre>
<p>結果:</p>
<pre><code>start
total: 75281071
end and cost: 1161 ms</code></pre>
<p>eg: 結合兩個檔案的內容</p>
<pre><code>package com.example
import scala.concurrent.Await
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
import scala.concurrent.TimeoutException
import scala.concurrent.duration.Duration
import scala.io.Source
import scala.util.control.NonFatal
import scala.util.Success
import scala.util.Failure
object FutureTest {
def readFile(file: String): StringBuilder = {
val ret = new StringBuilder
Source.fromFile(file).getLines() foreach { line =>
ret ++= (line + "\r\n")
}
ret
}
def main(args: Array[String]) {
println("start")
val time = System.currentTimeMillis()
/* flatMap (for) start */
val future1 = Future { readFile("ufo_awesome_1.tsv") }
val future2 = Future { readFile("ufo_awesome_2.tsv") }
val future3 = for (sb1 <- future1; sb2 <- future2) yield {
sb1.toString + "\r\n" + sb2.toString
}
val result = Await.result(future3, Duration.Inf)
println(s"total: ${result.length()}")
/* flatMap (for) end */
println(s"end and cost: ${System.currentTimeMillis() - time} ms")
}
}</code></pre>
<script type="text/x-mathjax-config">
if (typeof MathJaxListener !== 'undefined') {
MathJax.Hub.Register.StartupHook('End', function () {
MathJaxListener.invokeCallbackForKey_('End');
});
}
</script>
<script type="text/javascript" src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
</div>kigihttp://www.blogger.com/profile/09881273562560954871noreply@blogger.com0tag:blogger.com,1999:blog-6312720754439261813.post-80426297614470167482015-07-26T22:34:00.000+08:002015-07-26T22:34:01.280+08:00Scala Error Handle (Session 8)<div class='mymarkdown'>
<h1 id="toc_0">Scala Error Handle</h1>
<p>在 Function Language 中,Exception 是一種 Side Effect。在實際的環境中,只要跟 I/O 相關的,都會需要處理 Exception。Scala 除了保留原本 Java 的 <code>try</code>-<code>catch</code>-<code>finally</code>的機制外,提供下列三種方式來處理 Exception,再結合 <code>Option</code> 的方式,讓程式更可以專注在資料處理上。</p>
<h2 id="toc_1">Java: try - catch - finally</h2>
<p>一般寫法:</p>
<pre><code>try {
...
} catch {
case ex: Exception =>
...
} finally {
...
}</code></pre>
<p>千萬不要這麼寫:</p>
<pre><code>try {
...
} catch {
case _ =>
...
} finally {
...
}</code></pre>
<p>或</p>
<pre><code>try {
...
} catch {
case _: Throwable =>
...
} finally {
...
}</code></pre>
<p>偷懶的寫法:使用 <code>NonFatal</code></p>
<pre><code>import scala.util.control.NonFatal
try {
...
} catch {
case NonFatal(_) =>
...
} finally {
...
}</code></pre>
<p>Q: 為什麼不能使用:<code>case _ =></code> or <code>case _: Throwable =></code> ?</p>
<p>A: <code>Throwable</code> 有兩個 subclass: <code>Exception</code> 及 <code>Error</code>,一般在 Java 我們都是處理 <code>Exception</code>。<code>Error</code> 通常都是 JVM 發生重大錯誤時發出,如: <code>OutOfMemoryError</code>;此時應該是讓 JVM 中止執行,而不是繼續執行。</p>
<p><strong>NonFatal</strong> 不會處理以下錯誤:</p>
<ul>
<li>VirtualMachineError (包括 <strong>OutOfMemoryError</strong> and other fatal errors)</li>
<li>ThreadDeath</li>
<li>InterruptedException</li>
<li>LinkageError</li>
<li>ControlThrowable</li>
</ul>
<h2 id="toc_2">Try</h2>
<p><strong>Try</strong> 與 <strong>Option</strong> 相似,本身有兩個 subclass: <strong>Success</strong> 及 <strong>Failure</strong>。當沒有發生 Exception 時,會回傳 <strong>Success</strong> 否則回傳 <strong>Failure</strong>。</p>
<p>舉例:</p>
<pre><code>scala> import scala.util.Try
import scala.util.Try
scala> def parseInt(value: String) = Try { value.toInt }
parseInt: (value: String)scala.util.Try[Int]</code></pre>
<p>Try 常用的幾個 Function:</p>
<ul>
<li>map</li>
</ul>
<pre><code>scala> val t1 = parseInt("1000") map { _ * 2 }
t1: scala.util.Try[Int] = Success(2000)
scala> for (t1 <- parseInt("1000")) yield t1
res0: scala.util.Try[Int] = Success(1000)
scala> val t2 = parseInt("abc") map { _ * 2 }
t2: scala.util.Try[Int] = Failure(java.lang.NumberFormatException: For input string: "abc")
scala> for (t2 <- parseInt("abc")) yield t2
res1: scala.util.Try[Int] = Failure(java.lang.NumberFormatException: For input string: "abc")</code></pre>
<ul>
<li>recover</li>
</ul>
<pre><code>scala> import scala.util.control.NonFatal
import scala.util.control.NonFatal
scala> t1 recover { case NonFatal(_) => -1 }
res5: scala.util.Try[Int] = Success(2000)
scala> t2 recover { case NonFatal(_) => -1 }
res6: scala.util.Try[Int] = Success(-1)</code></pre>
<ul>
<li>toOption</li>
</ul>
<pre><code>scala> t1.toOption
res7: Option[Int] = Some(2000)
scala> t2.toOption
res8: Option[Int] = None</code></pre>
<ul>
<li>getOrElse</li>
</ul>
<pre><code>scala> t1.getOrElse(-1)
res9: Int = 2000
scala> t2.getOrElse(-1)
res10: Int = -1</code></pre>
<ul>
<li>註1: Try 使用 <strong>NonFatal</strong> 來處理 Exception。</li>
<li>註2: Option 及 Try 都是 ADT (Algebraic Data Type)</li>
</ul>
<h2 id="toc_3">Catch</h2>
<p>Catch 是用來處理 <code>catch</code> 及 <code>finally</code>。搭配 <code>Option</code> 及 <code>Either</code> 來處理 Exception。</p>
<pre><code>scala> import scala.util.control.Exception._
import scala.util.control.Exception._
scala> def parseInt(value: String) = nonFatalCatch[Int] opt { value.toInt }
parseInt: (value: String)Option[Int]
scala> def parseInt(value: String) = nonFatalCatch[Int] either { value.toInt }
parseInt: (value: String)scala.util.Either[Throwable,Int]
scala> def parseInt(value: String) = nonFatalCatch[Int] andFinally { println("finally") } opt { value.toInt }
parseInt: (value: String)Option[Int]
scala> def parseInt(value: String) = nonFatalCatch[Int] andFinally { println("finally") } opt { println("begin"); value.toInt }
parseInt: (value: String)Option[Int]
scala> parseInt("abc")
begin
finally
res2: Option[Int] = None
scala> parseInt("123")
begin
finally
res3: Option[Int] = Some(123)
scala> def parseInt(value: String) = catching(classOf[Exception]) opt { value.toInt }
parseInt: (value: String)Option[Int]
scala> parseInt("456")
res5: Option[Int] = Some(456)</code></pre>
<h2 id="toc_4">Either</h2>
<p><code>Either</code> 可以讓 Fuction 達到回傳不同型別資料效果。<code>Either</code> 有兩個 subclass: <code>Right</code> 及 <code>Left</code>。可以使用 <code>match</code>-<code>case</code> 來確認是回傳 <code>Right</code> or <code>Left</code>;進而了解是成功或失敗。</p>
<pre><code>scala> def parseInt(value: String) = try { Right(value.toInt) } catch { case ex: Exception => Left(value) }
parseInt: (value: String)Product with Serializable with scala.util.Either[String,Int]
scala> parseInt("123") match {
| case Right(v) => println(s"success ${v}")
| case Left(s) => println(s"failure ${s}")
| }
success 123
scala> parseInt("abc") match {
| case Right(v) => println(s"success ${v}")
| case Left(s) => println(s"failure ${s}")
| }
failure abc</code></pre>
<script type="text/x-mathjax-config">
if (typeof MathJaxListener !== 'undefined') {
MathJax.Hub.Register.StartupHook('End', function () {
MathJaxListener.invokeCallbackForKey_('End');
});
}
</script>
<script type="text/javascript" src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
</div>kigihttp://www.blogger.com/profile/09881273562560954871noreply@blogger.com0tag:blogger.com,1999:blog-6312720754439261813.post-89575652390185142642015-07-26T22:28:00.001+08:002015-07-26T22:28:17.014+08:00Map & Reduce in Scala Collection (Session 7)<div class='mymarkdown'>
<h1 id="toc_0">Map & Reduce in Scala Collection</h1>
<h2 id="toc_1">What's Map & Reudce</h2>
<p><strong>Map</strong> 是指將 collection 每個元素,經過指定的 function 處理,產生新的值 (也可以是另一個 Collection)</p>
<p>Map 的特性:</p>
<ul>
<li>Collection 的每一個元素都是獨立被處理,也就是說跟 collection 的其他元素無關。</li>
<li>經 Map 程序處理後,最後回傳的 collection 資料型別不變。比如說: List 經過 map 程序後,回傳值依然是 List,但其中的元素資料型別視處理的 function 而有所不同。</li>
<li>原本的 collection 內的元素值都不會被改變,最後是回傳一個<strong>新</strong>的 collection。</li>
</ul>
<p>延伸: <strong>flatMap</strong></p>
<p><strong>Reduce</strong> 是指將 collection 的元素做<strong>歸納</strong>處理後,回傳一個跟元素資料型別相同或者是<strong>supertype</strong> 的值。</p>
<p>Reduce 特性:</p>
<ul>
<li>指定的<strong>歸納</strong> function,第一次會處理兩個元素值。</li>
<li>每次處理後的結果,再跟下一個元素,再做一次處理,以此類推,直到所有的元素都被處理過。</li>
<li>處理的過程,不見得會依照預期的順序,因此指定的 function 如果沒有結合律的特性,也許結果會不如預期。</li>
<li>回傳最終處理的結果,且資料型別是跟元素相同或者是 supertype。</li>
</ul>
<p>註:結合律是 (x + y) + z = x + (y + z)。像四則運算的 \(+\) 與 \(\times\) 有結合律,但 \(-\) 與 \(\div\) 沒有。</p>
<p>延伸:<strong>reduceLeft</strong> & <strong>reduceRight</strong></p>
<p>舉例:輸入一個字串的 List,計算其中字串長度的總和。</p>
<pre><code>scala> val lst = List("ABC", "Hello, World!!!", "Apple", "Microsoft")
lst: List[String] = List(ABC, Hello, World!!!, Apple, Microsoft)
scala> val lenLst = lst map { _.length }
lenLst: List[Int] = List(3, 15, 5, 9)
scala> val total = lenLst reduce { _ + _ }
total: Int = 32</code></pre>
<p>或</p>
<pre><code>scala> val lst = List("ABC", "Hello, World!!!", "Apple", "Microsoft")
lst: List[String] = List(ABC, Hello, World!!!, Apple, Microsoft)
scala> lst map { _.length } reduce { _ + _ }
res7: Int = 32</code></pre>
<p>Function Languge Map-Reduce 的概念,後來被應用到 Hadoop 的 Map-Reduce。</p>
<h2 id="toc_2">Map, flatMap & for-yield</h2>
<p>Scala 的 <code>for-yield</code> 處理,實際上是轉成 <code>flatMap</code> 與 <code>map</code> 處理。</p>
<p>舉例:</p>
<p>一層迴圈</p>
<pre><code>scala> for (i <- 1 to 9) yield i + 1
res5: scala.collection.immutable.IndexedSeq[Int] = Vector(2, 3, 4, 5, 6, 7, 8, 9, 10)
scala> (1 to 9) map { _ + 1 }
res6: scala.collection.immutable.IndexedSeq[Int] = Vector(2, 3, 4, 5, 6, 7, 8, 9, 10)</code></pre>
<p>二層迴圈</p>
<pre><code>scala> for (i <- 1 to 9; j <- 1 to i) yield i * j
res7: scala.collection.immutable.IndexedSeq[Int] = Vector(1, 2, 4, 3, 6, 9, 4, 8, 12, 16, 5, 10, 15, 20, 25, 6, 12, 18, 24, 30, 36, 7, 14, 21, 28, 35, 42, 49, 8, 16, 24, 32, 40, 48, 56, 64, 9, 18, 27, 36, 45, 54, 63, 72, 81)
scala> (1 to 9) flatMap { i => (1 to i) map { i * _ } }
res8: scala.collection.immutable.IndexedSeq[Int] = Vector(1, 2, 4, 3, 6, 9, 4, 8, 12, 16, 5, 10, 15, 20, 25, 6, 12, 18, 24, 30, 36, 7, 14, 21, 28, 35, 42, 49, 8, 16, 24, 32, 40, 48, 56, 64, 9, 18, 27, 36, 45, 54, 63, 72, 81)</code></pre>
<p>Option 處理</p>
<p>單個:</p>
<pre><code>scala> val s1 = Some("s1")
s1: Some[String] = Some(s1)
scala> for (s <- s1) yield s + s
res11: Option[String] = Some(s1s1)
scala> s1 map { s => s + s }
res13: Option[String] = Some(s1s1)</code></pre>
<p>多個:</p>
<pre><code>scala> val s1 = Option("s1")
s1: Option[String] = Some(s1)
scala> val s2 = None
s2: None.type = None
scala> for (a <- s1; b <- s2) yield (a, b)
res15: Option[(String, Nothing)] = None
scala> s1 flatMap { a => s2 map { b => (a, b) } }
res17: Option[(String, Nothing)] = None
scala> for (a <- s1; b <- s2) yield a + b
res16: Option[String] = None
scala> s1 flatMap { a => s2 map { b => a + b } }
res18: Option[String] = None</code></pre>
<p><code>flatMap</code> or <code>for-yield</code> 很適合處理 <code>AND</code> 的情況</p>
<p>舉例:每個 Store 都有一個歸屬的 Store,目前要查詢歸屬的 Store 名稱。</p>
<pre><code>scala> case class Store(id: Int, name: String, belong: Int)
defined class Store
scala> val map = Map(0 -> Store(0, "3C", 0))
map: scala.collection.immutable.Map[Int,Store] = Map(0 -> Store(0,3C,0))</code></pre>
<p>if-else 的版本</p>
<pre><code>val s1 = map.get(0)
scala> if (s1.isDefined) {
| val s2 = map.get(s1.get.belong)
| if (s2.isDefined) Some(s2.get.name)
| else None
| } else None
res6: Option[String] = Some(3C)</code></pre>
<p>for-yield 版本</p>
<pre><code>scala> for (s1 <- map.get(0); s2 <- map.get(s1.belong)) yield s2.name
res0: Option[String] = Some(3C)
</code></pre>
<h2 id="toc_3">Collection 相關函數</h2>
<h3 id="toc_4">fold, foldLeft, foldRight</h3>
<p><code>fold</code> 與 <code>reduce</code> 很類似,<code>fold</code> 多了可以指定 <strong>初始值</strong></p>
<pre><code>scala> val lst = List(1, 2, 3, 4, 5, 6, 7, 8, 9)
lst: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9)
scala> lst reduce { _ + _ }
res20: Int = 45
scala> lst.fold(100) { _ + _ }
res25: Int = 145</code></pre>
<h3 id="toc_5">scan, scanLeft, scanRight</h3>
<p><code>scan</code> 可以指定 <strong>初始值</strong>,第一個元素與初始值處理的結果,再與第二個元素處理,以此類推,最後回傳原本 collection 的資料型別,初始值當作第一個元素。概念跟 <code>map</code> 有點像,<code>map</code> 是獨立處理每個元素,<code>scan</code> 會與上一次處理的結果有關。</p>
<pre><code>scala> val lst = List(1, 2, 3)
lst: List[Int] = List(1, 2, 3)
scala> lst map { _ + 1 }
res26: List[Int] = List(2, 3, 4)
scala> lst.scan(10) { (a, b) => println(a, b); a + b }
(10,1)
(11,2)
(13,3)
res30: List[Int] = List(10, 11, 13, 16)</code></pre>
<h3 id="toc_6">groupBy</h3>
<p><code>groupBy</code> 利用指定的 function 回傳值當作 <strong>key</strong>,自動依 key 分群,回傳一個 <code>Map</code>,Map 內的 value 資料型別,會與原來的 collection 相同。</p>
<pre><code>scala> val lst = List(1, 2, 3, 4, 5, 6, 7, 8, 9)
lst: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9)
scala> lst groupBy { _ % 3 }
res31: scala.collection.immutable.Map[Int,List[Int]] = Map(2 -> List(2, 5, 8), 1 -> List(1, 4, 7), 0 -> List(3, 6, 9))
scala> val arr = Array(1, 2, 3, 4, 5, 6, 7, 8, 9)
arr: Array[Int] = Array(1, 2, 3, 4, 5, 6, 7, 8, 9)
scala> arr groupBy { _ % 3 }
res32: scala.collection.immutable.Map[Int,Array[Int]] = Map(2 -> Array(2, 5, 8), 1 -> Array(1, 4, 7), 0 -> Array(3, 6, 9))
</code></pre>
<h3 id="toc_7">Zip</h3>
<p>將兩個 collection 中的元素,一對一的方式組成兩個元素的 <strong>tuple</strong>。行為很類似 <strong>拉鏈</strong>。</p>
<pre><code>scala> val lst1 = List("a", "b", "c", "d")
lst1: List[String] = List(a, b, c, d)
scala> val lst2 = List(1, 2, 3)
lst2: List[Int] = List(1, 2, 3)
scala> lst1 zip lst2
res0: List[(String, Int)] = List((a,1), (b,2), (c,3))
scala> lst1.zipWithIndex
res2: List[(String, Int)] = List((a,0), (b,1), (c,2), (d,3))
</code></pre>
<h1 id="toc_8">附錄</h1>
<h1 id="toc_9">Scala Variance & Bounds</h1>
<h2 id="toc_10">Varince</h2>
<p>Variance 主要是在討論</p>
<p>If \(T_1\) is subclass of \(T\), is Container[\(T_1\)] is subclass of Container[\(T\)] ?</p>
<table>
<thead>
<tr>
<th style="text-align: left">Variance</th>
<th style="text-align: left">Meaning</th>
<th style="text-align: left">Scala notation</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: left">covariant</td>
<td style="text-align: left">C[\(T_1\)] is subclass of C[\(T\)]</td>
<td style="text-align: left">[+T]</td>
</tr>
<tr>
<td style="text-align: left">contravariant</td>
<td style="text-align: left">C[\(T\)] is subclass of C[\(T_1\)]</td>
<td style="text-align: left">[-T]</td>
</tr>
<tr>
<td style="text-align: left">invariant</td>
<td style="text-align: left">C[\(T_1\)] and C[\(T\)] are not related</td>
<td style="text-align: left">[T]</td>
</tr>
</tbody>
</table>
<p>舉例:<strong>Covariant</strong></p>
<pre><code>scala> class Covariant[+A]
defined class Covariant
scala> val cv: Covariant[AnyRef] = new Covariant[String]
cv: Covariant[AnyRef] = Covariant@1fc2b765
scala> val cv: Covariant[String] = new Covariant[AnyRef]
<console>:8: error: type mismatch;
found : Covariant[AnyRef]
required: Covariant[String]
val cv: Covariant[String] = new Covariant[AnyRef]</code></pre>
<p>舉例:<strong>Contravariant</strong></p>
<pre><code>scala> class Contravariant[-A]
defined class Contravariant
scala> val cv: Contravariant[String] = new Contravariant[AnyRef]
cv: Contravariant[String] = Contravariant@7bc1a03d
scala> val cv: Contravariant[AnyRef] = new Contravariant[String]
<console>:8: error: type mismatch;
found : Contravariant[String]
required: Contravariant[AnyRef]
val cv: Contravariant[AnyRef] = new Contravariant[String]</code></pre>
<p>範例來自:<a href="https://twitter.github.io/scala_school/type-basics.html">Twitter's Scala School - Type & polymorphism basics</a></p>
<p>記憶方法:</p>
<p>\[
\forall T_1 \in +T, \text{then }T_1 \text{ is subclass of } T
\]</p>
<p>\[
\forall T_1 \in -T, \text{then }T_1 \text{ is superclass of } T
\]</p>
<p>\[
\begin{equation}
-T \\
\uparrow\\
T \\
\uparrow \\
+T
\end{equation}
\]</p>
<p>ContraVariant 案例:<code>Function1[-T, +R]</code></p>
<p>eg:</p>
<pre><code>scala> class Test[+A] { def test(a: A): String = a.toString }
<console>:7: error: covariant type A occurs in contravariant position in type A of value a
class Test[+A] { def test(a: A): String = a.toString }</code></pre>
<p>fix: </p>
<pre><code>scala> class Test[+A] { def test[B >: A](b: B): String = b.toString }
defined class Test</code></pre>
<h2 id="toc_11">Bounds</h2>
<h3 id="toc_12">Lower Type Bound</h3>
<p><code>A >: B</code> => <code>A</code> is superclass of <code>B</code>. <code>B</code> is the lower bound.</p>
<h3 id="toc_13">Upper Type Bound</h3>
<p><code>A <: B</code> => A is subclass of <code>B</code>. <code>B</code> is the upper bound.</p>
<p>參考:</p>
<ul>
<li><a href="https://twitter.github.io/scala_school/type-basics.html">Twitter's Scala School - Type & polymorphism basics</a></li>
</ul>
<script type="text/x-mathjax-config">
if (typeof MathJaxListener !== 'undefined') {
MathJax.Hub.Register.StartupHook('End', function () {
MathJaxListener.invokeCallbackForKey_('End');
});
}
</script>
<script type="text/javascript" src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
</div>kigihttp://www.blogger.com/profile/09881273562560954871noreply@blogger.com0tag:blogger.com,1999:blog-6312720754439261813.post-21854408944888205132015-07-26T22:25:00.001+08:002015-07-26T22:25:30.734+08:00Case Class and Pattern Match (Session 6)<div class='mymarkdown'>
<h1 id="toc_0">Case Class and Pattern Match</h1>
<h2 id="toc_1">Case Class</h2>
<p>宣告:</p>
<pre><code>case class Person(name: String, age: Int)</code></pre>
<p>Compiler 後,會有 <code>Person.class</code> 及 <code>Person$.class</code></p>
<p><strong>scalap -private Person</strong>: </p>
<pre><code>case class Person(name: scala.Predef.String, age: scala.Int) extends scala.AnyRef with scala.Product with scala.Serializable {
val name: scala.Predef.String = { /* compiled code */ }
val age: scala.Int = { /* compiled code */ }
def copy(name: scala.Predef.String, age: scala.Int): Person = { /* compiled code */ }
override def productPrefix: java.lang.String = { /* compiled code */ }
def productArity: scala.Int = { /* compiled code */ }
def productElement(x$1: scala.Int): scala.Any = { /* compiled code */ }
override def productIterator: scala.collection.Iterator[scala.Any] = { /* compiled code */ }
def canEqual(x$1: scala.Any): scala.Boolean = { /* compiled code */ }
override def hashCode(): scala.Int = { /* compiled code */ }
override def toString(): java.lang.String = { /* compiled code */ }
override def equals(x$1: scala.Any): scala.Boolean = { /* compiled code */ }
}
object Person extends scala.runtime.AbstractFunction2[scala.Predef.String, scala.Int, Person] with scala.Serializable {
def this() = { /* compiled code */ }
final override def toString(): java.lang.String = { /* compiled code */ }
def apply(name: scala.Predef.String, age: scala.Int): Person = { /* compiled code */ }
def unapply(x$0: Person): scala.Option[scala.Tuple2[scala.Predef.String, scala.Int]] = { /* compiled code */ }
private def readResolve(): java.lang.Object = { /* compiled code */ }
}</code></pre>
<p><strong>javap -p Person</strong></p>
<pre><code>Compiled from "Person.scala"
public class Person implements scala.Product,scala.Serializable {
private final java.lang.String name;
private final int age;
public static scala.Option<scala.Tuple2<java.lang.String, java.lang.Object>> unapply(Person);
public static Person apply(java.lang.String, int);
public static scala.Function1<scala.Tuple2<java.lang.String, java.lang.Object>, Person> tupled();
public static scala.Function1<java.lang.String, scala.Function1<java.lang.Object, Person>> curried();
public java.lang.String name();
public int age();
public Person copy(java.lang.String, int);
public java.lang.String copy$default$1();
public int copy$default$2();
public java.lang.String productPrefix();
public int productArity();
public java.lang.Object productElement(int);
public scala.collection.Iterator<java.lang.Object> productIterator();
public boolean canEqual(java.lang.Object);
public int hashCode();
public java.lang.String toString();
public boolean equals(java.lang.Object);
public Person(java.lang.String, int);
}</code></pre>
<p><strong>javap -p Person$</strong></p>
<pre><code>Compiled from "Person.scala"
public final class Person$ extends scala.runtime.AbstractFunction2<java.lang.String, java.lang.Object, Person> implements scala.Serializable {
public static final Person$ MODULE$;
public static {};
public final java.lang.String toString();
public Person apply(java.lang.String, int);
public scala.Option<scala.Tuple2<java.lang.String, java.lang.Object>> unapply(Person);
private java.lang.Object readResolve();
public java.lang.Object apply(java.lang.Object, java.lang.Object);
private Person$();
}</code></pre>
<p>需注意的重點:</p>
<ul>
<li>由 <code>scalap</code> 及 <code>javap</code> 來看,在宣告 <code>case class</code> 後,會產生兩個 class。</li>
<li>當我們在產生 case class 時,是呼叫 <code>object</code> (singeton) 的 <code>apply</code> function.</li>
<li>case class contructor 的參數,會自動變成 <strong>read only</strong> 的 member data.</li>
</ul>
<pre><code>scala> case class Person(name: String, age: Int)
defined class Person
scala> val p1 = Person("abc", 10)
p1: Person = Person(abc,10)</code></pre>
<p>與 Pattern Match 有直接關係的 function: <code>apply</code> and <code>unapply</code>. 以 <code>Person</code> 為例:</p>
<pre><code>def apply(name: scala.Predef.String, age: scala.Int): Person = { /* compiled code */ }
def unapply(x$0: Person): scala.Option[scala.Tuple2[scala.Predef.String, scala.Int]] = { /* compiled code */ }</code></pre>
<h2 id="toc_2">Pattern Match</h2>
<p>上例 <code>Person</code> 的 Pattern Match 範例:</p>
<pre><code>scala> p1 match {
| case Person(n, a) => println(n, a)
| case _ => println("not match")
| }
(abc,10)</code></pre>
<h3 id="toc_3">Extractor</h3>
<p>一個 class or object 有以下之一的 function 時,就可以稱作 <strong>Extractor</strong>。</p>
<ul>
<li>unapply</li>
<li>unapplySeq</li>
</ul>
<p>這類的 function ,稱為 <strong>extraction</strong>;反之,<code>apply</code> 則稱為 <strong>injection</strong>。</p>
<p>Extractor 只要有實作 <code>unapply</code> or <code>unapplySeq</code> 即可;但如果 Extractor 沒有實作 <code>apply</code>, 則 <code>unapply</code> 回傳型別必須是 <code>Boolean</code>。</p>
<p><code>unapplySeq</code> 是用在 <strong>variable argument</strong> 也就是類似 <code>func(lst: String*)</code>。</p>
<p>Extractor 可以是 <code>object</code> or <code>class</code>。<code>class</code> 可以存當時的條件,但 <code>object</code> 則沒有這樣的效果 (因為 object 是 singleton,無法存每次不同的比對條件)</p>
<h3 id="toc_4">Pattern, Extractor, and Binding</h3>
<h4 id="toc_5">Extractor only with extraction and binding</h4>
<pre><code>package com.example
object EMail {
/* Injection */
def apply(u: String, d: String) = s"${u}@${d}"
/* Extraction */
def unapply(s: String): Option[(String, String)] = {
println("EMail.unapply")
var parts = s.split("@")
if (parts.length == 2) Some(parts(0), parts(1)) else None
}
}
/* Extraction */
object UpperCase {
def unapply(s: String): Boolean = {
println("UpperCase.unapply")
s == s.toUpperCase()
}
}
object PatternTest {
def main(args: Array[String]) {
"Test@test.com" match {
case EMail(user @ UpperCase(), domain) => println(user, domain) /* 注意:UpperCase 後面一定要加 () (括號) */
case _ => println("not match")
}
"TEST@test.com" match {
case EMail(user @ UpperCase(), domain) => println(user, domain)
case _ => println("not match")
}
}
}</code></pre>
<p>執行結果:</p>
<pre><code>EMail.unapply
UpperCase.unapply
not match
EMail.unapply
UpperCase.unapply
(TEST,test.com)</code></pre>
<p>當在執行 pattern match 至 <code>case EMail</code> 時,會去呼叫 <code>EMail.unapply(s: String)</code> 看是否符合;當符合時,再呼叫 <code>UpperCase.unapply(s: String)</code>。</p>
<p><code>Test@test.com</code> 結果是 <code>not match</code>, 因為在 <code>UpperCase</code> 是 <code>false</code>. <code>TEST@test.com</code> 則是 <code>(TEST, test.com)</code></p>
<p>截自:<a href="http://www.amazon.com/Programming-Scala-Comprehensive-Step-Step/dp/0981531644">Programming in Scala: A Comprehensive Step-by-Step Guide, 2nd Edition</a></p>
<h4 id="toc_6">Extractor with variable arguement</h4>
<pre><code>/* Extraction Only*/
class Between(val min: Int, val max: Int) {
def unapplySeq(value: Int): Option[List[Int]] =
if (min <= value && value <= max) Some(List(min, value, max))
else None
}
object PatternTest {
def main(args: Array[String]) {
val between5and15 = new Between(5, 15)
10 match {
case between5and15(min, value, max) => println(value)
case _ => println("not match")
}
20 match {
case between5and15(min, value, max) => println(value)
case _ => println("not match")
}
}
}</code></pre>
<p>執行結果:</p>
<pre><code>10
not match</code></pre>
<p>因為 <code>Between</code> 的 <code>unapplySeq</code> 回傳是 <code>List(min, value, max)</code>,所以比對的 pattern 就必須是 List 的 pattern,像 <code>(min, value, max)</code> or <code>(_, value, max)</code> or <code>(min, _*)</code></p>
<h4 id="toc_7">Extractor with binding</h4>
<pre><code>class Between(val min: Int, val max: Int) {
def unapplySeq(value: Int): Option[List[Int]] =
if (min <= value && value <= max) Some(List(min, value, max))
else None
}
object PatternTest {
def main(args: Array[String]) {
(50, 10) match {
case (n @ between5and15(_*), _) => println("first match " + n)
case (_, m @ between5and15(_*)) => println("second match " + m)
case _ => println("not match")
}
}
}</code></pre>
<p>執行結果:</p>
<pre><code>second match 10</code></pre>
<p>Extractor 用在 binding 時,要注意要附上比對的 pattern (ex: <code>between5and15(_*)</code>),如果沒寫對,會比對失敗。比如說:把 <code>(_, m @ between5and15(_*))</code> 改成 <code>case (_, m @ between5and15())</code>, 雖然 m (m = 10) 在 5 ~ 15,但會比對失敗。</p>
<h2 id="toc_8">Pattern and Regex</h2>
<p>Scala 的 Regex 有實作 <code>unapplySeq</code>, Regex 搭配 Pattern 非常好用。</p>
<pre><code>object RegexTest {
def main(args: Array[String]) {
val digits = """(\d+)-(\d+)""".r
"123-456" match {
case digits(a, b) => println(a, b)
case _ => println("not match")
}
"123456" match {
case digits(a, b) => println(a, b)
case _ => println("not match")
}
"abc-456" match {
case digits(a, b) => println(a, b)
case _ => println("not match")
}
}
}</code></pre>
<p>執行結果:</p>
<pre><code>(123,456)
not match
not match</code></pre>
<p>因為 <code>digits</code> 有用到 <code>group</code>,所以 pattern 會是 <code>digits(a, b)</code>。如果把 <code>val digits = """(\d+)-(\d+)""".r</code> 改成 <code>val digits = """\d+-\d+""".r</code> 不使用 group 時,因為比對的 pattern 改變 (<code>digits(a, b)</code> -> <code>digits()</code>),所以上面的三個比對都會是 <code>not match</code>。需要將程式改成如下,才會正確</p>
<pre><code>val digits = """\d+-\d+""".r
"123-456" match {
case digits() => println("ok")
case _ => println("not match")
}
</code></pre>
<p>所以使用 <code>Regex</code> 時,儘量用 <code>group</code> 的功能,在系統設計時,彈性會比較大。</p>
<h3 id="toc_9">Regex and Binding</h3>
<pre><code>val digits = """(\d+)-(\d+)-(\d+)""".r
("123-abc-789", "123-456-789") match {
case (_ @ digits(a, _*), _) => println(a)
case (_, _ @ digits(a, b, c)) => println(a, b, c)
case _ => println("not match")
}</code></pre>
<p>用 Binding 時,一樣要注意比對的 pattern,如: <code>digits(a, _*)</code>, <code>digits(a, b, c)</code></p>
<h2 id="toc_10">Case Class, Patch Match and Algebraic Data Type</h2>
<pre><code>sealed trait Tree
object Empty extends Tree
case class Leaf(value: Int) extends Tree
case class Node(left: Tree, right: Tree) extends Tree
object TreeTest {
val depth: PartialFunction[Tree, Int] = {
case Empty => 0
case Leaf(value) => 1
case Node(l, r) => 1 + Seq(depth(l), depth(r)).max
}
def max(tree: Tree): Int = tree match {
case Empty => Int.MinValue
case Leaf(value) => value
case Node(l, r) => Seq(max(r), max(l)).max
}
def main(args: Array[String]) {
val tree = Node(
Node(
Leaf(1),
Node(Leaf(3), Leaf(4))
),
Node(
Node(Leaf(100), Node(Leaf(6), Leaf(7))),
Leaf(2)
)
)
println(depth(tree))
println(max(tree))
}
}</code></pre>
<p>注意:使用 <code>sealed</code> 時,子類別都要與父類別放在同一個原始碼中,且如果在 pattern match 少比對一種子類別時,會出現<strong>警告</strong>。</p>
<p>範例修改自:</p>
<ul>
<li><p><a href="http://twitter.github.io/effectivescala/#Functional%20programming-Case%20classes%20as%20algebraic%20data%20types">Twitter Effective Scala: Functional programming - Case classes as algebraic data types</a></p></li>
<li><p><a href="https://en.wikipedia.org/wiki/Algebraic_data_type">Wiki: Algebraic data type</a></p></li>
</ul>
<p>進階:</p>
<p><a href="https://en.wikipedia.org/wiki/Algebraic_data_type">Wiki: Algebric Data Type</a></p>
<script type="text/x-mathjax-config">
if (typeof MathJaxListener !== 'undefined') {
MathJax.Hub.Register.StartupHook('End', function () {
MathJaxListener.invokeCallbackForKey_('End');
});
}
</script>
<script type="text/javascript" src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
</div>kigihttp://www.blogger.com/profile/09881273562560954871noreply@blogger.com0tag:blogger.com,1999:blog-6312720754439261813.post-60718844665629235012015-07-26T22:22:00.000+08:002015-07-26T22:22:52.721+08:00Scala Functional Language 簡介 (Session 5)<div class='mymarkdown'>
<h1 id="toc_0">Scala Functional Language 簡介</h1>
<p>Functional Language 其實很早就有了,只是 OOP 概念比較能讓人接受,因此 Functional Language 就被忽略。近來大數據需要做大量平行與分散式的運算,Functional Lanauge 才又被重視。</p>
<p>Functional Language 簡單來說,就是:</p>
<p>Treats computation as the evaluation of <strong>mathematical functions</strong> and avoids <strong>changing-state and mutable data (Side Effects)</strong></p>
<p>截自 <a href="http://en.wikipedia.org/wiki/Functional_programming">Wiki: Functional Lanauge</a></p>
<h2 id="toc_1">名詞解釋</h2>
<h3 id="toc_2">Mathematical Functions</h3>
<p>Mathematical Functions 簡單來說,就是兩個集合間的關係,且每一個輸入值,只會對應到一個輸出值。</p>
<p>最簡單的數學函數的表示式:</p>
<p>\[f: X \mapsto Y\]</p>
<p>比如說:Square</p>
<p>\[Square: Int \mapsto Int \]
\[f(x) = x^2\]</p>
<p>比如 \(f(3) = 9\), \(f(-3) = 9\) ,每次計算 \(f(3)\) 一定都會是 <strong>9</strong> 不會變成其他值。</p>
<h3 id="toc_3">Side Effects</h3>
<p>程式的函式有以下的行為時,就會稱該函式有 <strong>Side Effects</strong>。</p>
<ul>
<li>Reassigning a variable (val v.s. var)</li>
<li>Modify a data structure in place (mutable v.s. immutable)</li>
<li>Setting a field on an object (change object state)
<ul>
<li>這裏指的 object 是 OOP 的 Object 不是 Scala 的 object (singleton)</li>
<li>OOP 修改物件的值,在程式語言的術語:改變物件的狀態,如上說的 <strong>changing-state</strong></li>
</ul></li>
<li>Throwing an exception or halting with error</li>
<li>Printing to the console or reading user input (I/O)</li>
<li>Reading from or write to a file (I/O)</li>
<li>Drawing on the screen (I/O)</li>
</ul>
<p>截自 <a href="http://www.amazon.com/Functional-Programming-Scala-Paul-Chiusano/dp/1617290653">Functional Language in Scala</a></p>
<p>就實際狀況來說,我們寫程式不可能不去碰 I/O。</p>
<h3 id="toc_4">Purely Functions</h3>
<p><strong>Purely functional functions (or expressions) have no side effects (memory or I/O).</strong></p>
<p>截自 <a href="http://en.wikipedia.org/wiki/Functional_programming#Pure_functions">Wiki: Purely Function</a></p>
<h3 id="toc_5">Referential Transparency (RT)</h3>
<p><strong>An expression is said to be referentially transparent if it can be replaced with its value without changing the behavior of a program</strong></p>
<p>截自 <a href="http://en.wikipedia.org/wiki/Referential_transparency_%28computer_science%29">Wiki: Referential Transparency</a></p>
<p>簡單來說,程式碼中的變數,可以用此變數的值或運算式子來取代,而且不會改變輸出的結果。</p>
<p>舉例來說:String 是 immutable,當每次呼叫 <code>reverse</code> 時,都會回傳固定的值。</p>
<pre><code>scala> val x = "Hello, World"
x: String = Hello, World
scala> val r1 = x.reverse
r1: String = dlroW ,olleH
scala> val r2 = x.reverse
r2: String = dlroW ,olleH</code></pre>
<p>此時,可以將上述的 <code>x</code> 直接替換成 <code>Hello, World</code>,程式的結果都不會被改變。此特性,就是 <strong>Referential Transparency</strong>。如下:</p>
<pre><code>scala> val r1 = "Hello, World".reverse
r1: String = dlroW ,olleH
scala> val r2 = "Hello, World".reverse
r2: String = dlroW ,olleH</code></pre>
<p>另舉反例:StringBuilder 是 mutable,<code>append</code> 會修改 StringBuffer 內的值 (change object state)。</p>
<pre><code>scala> val x = new StringBuilder("Hello")
x: StringBuilder = Hello
scala> val y = x.append(", World")
y: StringBuilder = Hello, World
scala> val r1 = y.toString
r1: String = Hello, World
scala> val r2 = y.toString
r2: String = Hello, World</code></pre>
<p>當將 <code>y</code> 替換成 <code>x.append(", World")</code> 時:</p>
<pre><code>scala> val r1 = x.append(", World").toString
r1: String = Hello, World
scala> val r2 = x.append(", World").toString
r2: String = Hello, World, World</code></pre>
<p>此時 <code>r1</code> 及 <code>r2</code> 的值並不一致,這樣子就沒有 <strong>Referential Transparency</strong> 。</p>
<p>範例截自: <a href="http://www.amazon.com/Functional-Programming-Scala-Paul-Chiusano/dp/1617290653">Functional Language in Scala</a></p>
<h4 id="toc_6">為什麼 Referential Transparency 如此重要</h4>
<p>當程式設計都符合 Referential Transparency 時,就代表程式可以分散在不同 Thread, CPU核心,甚至不同主機上處理(空間),而且不論什麼時候被處理(時間),都不會影響輸出的結果。</p>
<p><strong>Funcational Language 程式設計的終極目標就是 Referential Transparency。</strong></p>
<h3 id="toc_7">First-Class Function and High Order Function</h3>
<h4 id="toc_8">First-Class Function</h4>
<p>一個程式語言有 <strong>First-Class Function</strong> 特性,是指此程式語言將 <strong>Function</strong> 當作是一種資料型態。</p>
<p>在 Scala 中,有定義 <code>Function</code> 這個 class。如下:</p>
<pre><code>scala> val max = (x: Int, y:Int) => if (x > y) x else y
max: (Int, Int) => Int = <function2>
scala> max(3, 4)
res5: Int = 4</code></pre>
<h4 id="toc_9">High Order Function</h4>
<p>Hight Order Function 是指 Function 其中一個參數的資料型別是 Function。比如 <code>List</code> 的 <code>foreach</code>。</p>
<pre><code>scala> List(1, 2, 3, 4) foreach { x => println(x + x) }
2
4
6
8</code></pre>
<h3 id="toc_10">Function Composition</h3>
<p>數學的複合函數:</p>
<p>\[f: X \mapsto Y\]</p>
<p>\[g: Y \mapsto Z\]</p>
<p>\[ g \circ f: X \mapsto Z\]</p>
<p>\[ (g \circ f )(x) = g(f(x))\]</p>
<p>在 Scala 上的實作,有 <code>compose</code> 及 <code>andThen</code></p>
<p><code>f andThen g</code> 等同於 \( g \circ f \)</p>
<p><code>f compose g</code> 等同於 \( f \circ g \)</p>
<p>eg:</p>
<pre><code>scala> val f = (x: Int) => x * x
f: Int => Int = <function1>
scala> val g = (x: Int) => x + 1
g: Int => Int = <function1>
scala> val goff = f andThen g
goff: Int => Int = <function1>
scala> goff(10)
res10: Int = 101
scala> val fofg = f compose g
fofg: Int => Int = <function1>
scala> fofg(10)
res11: Int = 121</code></pre>
<h4 id="toc_11">轉成 Function Class</h4>
<p>一般會用 <code>def</code> 宣告 function;可以使用 <strong><code>_</code></strong> 轉換成 Function Class。如下:</p>
<pre><code>scala> def f(x: Int) = x * x
f: (x: Int)Int
scala> def g(x: Int) = x + 1
g: (x: Int)Int
scala> f andThen g
<console>:10: error: missing arguments for method f;
follow this method with `_' if you want to treat it as a partially applied function
f andThen g
^
<console>:10: error: missing arguments for method g;
follow this method with `_' if you want to treat it as a partially applied function
f andThen g
^
scala> f _ andThen g _
res1: Int => Int = <function1></code></pre>
<h3 id="toc_12">Partially Applied Function</h3>
<pre><code>def sum(x: Int, y: Int, z: Int) = x + y + z
sum: (x: Int, y: Int, z: Int)Int
scala> val a = sum _
a: (Int, Int, Int) => Int = <function3>
scala> val b = sum(1, _: Int, 3)
b: Int => Int = <function1>
scala> b(2)
res1: Int = 6</code></pre>
<h3 id="toc_13">Closure</h3>
<p>A function object that captures free variables, and is said to be “closed” over the variables visible at the time it is created.</p>
<p>舉例:</p>
<pre><code>scala> var more = 10
more: Int = 10
scala> val addMore = (x: Int) => x + more
addMore: Int => Int = <function1></code></pre>
<p><code>addMore</code> 是一個 <strong>Closure</strong>. <code>more</code> 這個變數是 <strong>free variable</strong>. <code>x</code> 是 <strong>bounded variable</strong>.</p>
<h3 id="toc_14">Currying</h3>
<p>假設有個 function 由兩個以上的集合對應到一個集合:</p>
<p>\[f: X \times Y \mapsto Z\]</p>
<p>比如說:</p>
<p>\[f(x, y) = \frac{y}{x}\]</p>
<p>我們可以定義一個 <strong>Curring Function</strong> :</p>
<p>\[h(x) = y \mapsto f(x, y)\]</p>
<p>\(h(x)\) 是一個 Function ,它的輸入值是 x ,回傳值是 Function 。</p>
<p>比如說:</p>
<p>\[h(2) = y \mapsto f(2, y)\]</p>
<p>這時候的 \( h(2) \) 是一個 Function:</p>
<p>\[ f(2, y) = \frac {y}{2} \]</p>
<p>此時,我們可以再定義一個 Function: \(g(y) \)</p>
<p>\[g(y) = h(2) = y \mapsto f(2, y)\]</p>
<p>也就是</p>
<p>\[g(y) = f(2, y) = \frac {y}{2}\]</p>
<p>在 Scala 的實作:</p>
<p>定義 \( f: X \times Y \mapsto Z \)</p>
<pre><code>scala> def f(x: Int)(y: Int) = y / x
f: (x: Int)(y: Int)Int
scala> f(4)(2)
res7: Int = 0
scala> f(2)(4)
res8: Int = 2</code></pre>
<p>定義 \( g(y) = h(2) = y \mapsto f(2, y) \) i.e.
\[g(y) = f(2, y) = \frac {y}{2} \]</p>
<pre><code>scala> val h = f(2) _
h: Int => Int = <function1></code></pre>
<p>當 \( y = 4 \) 時,</p>
<p>\[g(4) = f(2, 4) = \frac {4} {2} = 2\]</p>
<pre><code>scala> h(4)
res9: Int = 2</code></pre>
<p>範例截自 <a href="http://en.wikipedia.org/wiki/Currying">Wiki: Curring</a></p>
<p>另一種使用時機:</p>
<pre><code>scala> def modN(n: Int)(x: Int) = ((x % n) == 0)
modN: (n: Int)(x: Int)Boolean
scala> val nums = List(1, 2, 3, 4, 5, 6, 7, 8)
nums: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8)
scala> nums filter { modN(2) }
res10: List[Int] = List(2, 4, 6, 8)
scala> nums filter { modN(3) }
res11: List[Int] = List(3, 6)</code></pre>
<p>數學式對應:</p>
<p>\[modN: Int \times Int \mapsto Boolean\]</p>
<p>\[modN(n, x) \Rightarrow ((x \bmod n ) == 0)\]</p>
<p>\[mod2(x) = modN(2, x) \Rightarrow ((x \bmod 2) == 0)\]</p>
<p>\[mod3(x) = modN(3, x) \Rightarrow ((x \bmod 3) == 0)\]</p>
<p>範例截自:<a href="http://docs.scala-lang.org/tutorials/tour/currying.html">Scala Document: Currying</a></p>
<h3 id="toc_15">Scala Partial Function</h3>
<p>一般定義 Function 都會去處理輸入值的所有情況。比如說:</p>
<pre><code>def plusOne(x: Int) = x + 1</code></pre>
<p>所有 Int 整數值,傳進 <code>plusOne</code> 都會被處理。</p>
<p><strong>Partial Function</strong> 換言之就是只處理某些狀況下的值。</p>
<p>定義:</p>
<pre><code>scala> val one: PartialFunction[Int, String] = { case 1 => "one" }
one: PartialFunction[Int,String] = <function1></code></pre>
<p>使用:如果輸入沒有要處理的值時,會出現 <strong>Exception</strong>。比如 <strong>1</strong> 有定義,但 <strong>2</strong> 沒有,所以輸入 <strong>1</strong> 沒問題,輸入 <strong>2</strong> 就會有 <strong>Exception</strong>。</p>
<pre><code>scala> one(1)
res0: String = one
scala> one(2)
scala.MatchError: 2 (of class java.lang.Integer)
at scala.PartialFunction$$anon$1.apply(PartialFunction.scala:253)
at scala.PartialFunction$$anon$1.apply(PartialFunction.scala:251)
at $anonfun$1.applyOrElse(<console>:7)
at $anonfun$1.applyOrElse(<console>:7)
at scala.runtime.AbstractPartialFunction.apply(AbstractPartialFunction.scala:36)
... 33 elided</code></pre>
<p>查詢輸入值,是否已在處理的範圍內:</p>
<pre><code>scala> one.isDefinedAt(1)
res2: Boolean = true
scala> one.isDefinedAt(2)
res3: Boolean = false</code></pre>
<h4 id="toc_16">Composition of Partial Function</h4>
<p>可以使用多個 Partial Function 組成一個複合函數。</p>
<pre><code>scala> val two: PartialFunction[Int, String] = { case 2 => "two" }
two: PartialFunction[Int,String] = <function1>
scala> val three: PartialFunction[Int, String] = { case 3 => "three" }
three: PartialFunction[Int,String] = <function1>
scala> val wildcard: PartialFunction[Int, String] = { case _ => "something else" }
wildcard: PartialFunction[Int,String] = <function1>
scala> val partial = one orElse two orElse three orElse wildcard
partial: PartialFunction[Int,String] = <function1>
scala> partial(5)
res4: String = something else
scala> partial(3)
res5: String = three
scala> partial(2)
res6: String = two
scala> partial(1)
res7: String = one
scala> partial(0)
res8: String = something else
scala> partial.isDefinedAt(10)
res9: Boolean = true
scala> partial.isDefinedAt(1000)
res10: Boolean = true</code></pre>
<p>範例截自:<a href="https://twitter.github.io/scala_school/pattern-matching-and-functional-composition.html#PartialFunction">Twitter Scala School - Partial Function</a></p>
<h3 id="toc_17">總結</h3>
<p>開始使用 Functional Lanague 時,思維需要做改變,程式設計時,以往用 OO 處理的設計,要轉換到是否可以切割成 Function 來處理,尤其是 Function 要符合數學函數或 Purly Function 的定義,Functional Language 程式的設計思維,會更倚重數學邏輯。</p>
<p>進階:</p>
<ul>
<li><a href="http://en.wikipedia.org/wiki/Monad_(functional_programming)">Wiki: Monad</a></li>
<li><a href="https://zh-tw.coursera.org/course/reactive">Coursera: 響應式編程原理 - Week 1</a></li>
</ul>
<script type="text/x-mathjax-config">
if (typeof MathJaxListener !== 'undefined') {
MathJax.Hub.Register.StartupHook('End', function () {
MathJaxListener.invokeCallbackForKey_('End');
});
}
</script>
<script type="text/javascript" src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
</div>kigihttp://www.blogger.com/profile/09881273562560954871noreply@blogger.com1tag:blogger.com,1999:blog-6312720754439261813.post-67420148818608442742015-07-26T22:18:00.000+08:002015-07-26T22:18:15.762+08:00Scala Collection (Session 4)<div class='mymarkdown'>
<h1 id="toc_0">Scala Collection</h1>
<p>Scala Collection 功能比 Java 強大很多。在語法上也差異很大。因此在學習過程,建議直接依書上的範例做過一遍。</p>
<p>建議閱讀資料:</p>
<ul>
<li><a href="http://twitter.github.io/effectivescala/#Collections">Twitter Effective Scala - Collections</a></li>
<li><a href="http://www.scala-lang.org/docu/files/collections-api/collections.html">Scala Collection API</a></li>
<li><a href="http://www.scala-lang.org/docu/files/collections-api/collections_40.html">Scala Collection API - Performance Characteristics</a></li>
<li><a href="http://en.wikipedia.org/wiki/Referential_transparency_%28computer_science%29">Referential Transparency</a></li>
</ul>
<h2 id="toc_1">Array</h2>
<p>Scala 有 <code>Array</code> 的 Class,語法上與 Java 差異很大。
eg:</p>
<p>Java String Array: <code>String[] test = Strig[5];</code></p>
<p>Scala String Array: <code>val test = Array.fill(5) { "" }</code></p>
<p>Q: Array 特性?!</p>
<p>Scala <code>Array</code> 是一組 mutable indexed 集合。</p>
<p>宣告:</p>
<pre><code>scala> val arr1 = Array(1, 2, 3)
arr1: Array[Int] = Array(1, 2, 3)
scala> val arr2 = Array("a", "b", "c")
arr2: Array[String] = Array(a, b, c)
scala> val arr3 = Array.fill(3) { math.random }
arr3: Array[Double] = Array(0.6325626123459638, 0.7135533929240558, 0.6461315533714135)
scala> class Test
defined class Test
scala> val arrTest = Array.fill[Test](3) { null }
arrTest: Array[Test] = Array(null, null, null)</code></pre>
<p>取值:</p>
<pre><code>arr1(1)</code></pre>
<h2 id="toc_2">Immutable v.s. Mutable</h2>
<p>Scala Collection Class 分成 <strong>Immutable</strong> 及 <strong>Mutable</strong>。兩者主要差別是 <strong>Mutale</strong> 可以修改集合裏面的內容;但 <strong>Immutable</strong> 不行。使用類似 <code>append</code> 時,<code>Immutable</code> 的集合,會回傳一個新的集合,而非就現有的集合擴充,在使用時,要特別小心,以免造成記憶體浪費。</p>
<p>Mutable Collection Class 都會有 <code>toXXXX</code> 去轉成 Immutable Collection。</p>
<p>p.s. 在 Java 最常用的 immutable 是 <strong>String</strong></p>
<h3 id="toc_3">Scala Collection Hierarchy</h3>
<p><img src="http://www.scala-lang.org/docu/files/collections-api/collections.png" alt="Scala Collection"></p>
<h3 id="toc_4">Scala Immutable Collection</h3>
<p><img src="http://www.scala-lang.org/docu/files/collections-api/collections.immutable.png" alt="Scala Immutable Collection"></p>
<h3 id="toc_5">Scala Mutable Collection</h3>
<p><img src="http://www.scala-lang.org/docu/files/collections-api/collections.mutable.png" alt="Scala Mutable Collection"></p>
<h3 id="toc_6">效能問題</h3>
<p>Scala 的 Collection 有很完整的繼承結構,在效能上,越底層的 Class 效能會越差。因此使用時,要確定好你需要的功能是什麼。如果只用到 <code>Seq</code> ,就不需要用到 <code>List</code>。</p>
<h3 id="toc_7">Immutable or Mutable</h3>
<p>用那一個?</p>
<ul>
<li>基本上,如果宣告後,就不會再更動內容,就直接用 <strong>Immutable</strong></li>
<li>需要用程式處理初始化 (如:從 DB 讀資料,組出 List);這類 case,一開始會用 mutable,但做完初始化後,即刻轉成 immutable。</li>
<li>API 的參數設計,儘量使用 immutable 型態。</li>
</ul>
<p>Q: 為什麼建議使用 <strong>Immutable</strong>?</p>
<p>A: 因為 Muli-Thread。學 Scala 最主要的目的,就是加速開發平行運算的程式,也就是 multi-thread 的程式。Immutable Read Only 的特性,使得有 <strong>Thread-Safe</strong> 效果,因此在程式撰寫時,建議儘量使用 Immutable class。</p>
<h3 id="toc_8">Twitter Coding Style</h3>
<p>由於 collection class 在 mutable 及 immutable 都會有。所以在使用時,為了避免誤用,Twitter 建議明確指出是目前是使用那一種。如完整指出使用 <code>mutable</code>。<strong>不要使用</strong> <code>import scala.collection.mutable._</code> </p>
<p>eg:</p>
<pre><code>import scala.collection.mutable
val set = mutable.Set()
</code></pre>
<h2 id="toc_9">常用的 Collection Sample</h2>
<h3 id="toc_10">List</h3>
<p>宣告:</p>
<pre><code>scala> val colors = List("red", "blue", "green")
colors: List[String] = List(red, blue, green)</code></pre>
<p>取頭:</p>
<pre><code>scala> colors.head
res0: String = red</code></pre>
<p>去頭:</p>
<pre><code>scala> colors.tail
res1: List[String] = List(blue, green)</code></pre>
<p>取其中一個值:</p>
<pre><code>scala> colors(1)
res2: String = blue</code></pre>
<p>取最後一個:</p>
<pre><code>scala> colors.last
res5: String = green</code></pre>
<p>去掉最後一個:</p>
<pre><code>scala> colors.init
res4: List[String] = List(red, blue)</code></pre>
<p>長度:</p>
<pre><code>scala> colors.length
res8: Int = 3</code></pre>
<p>串成字串:</p>
<pre><code>scala> colors.mkString(",")
res9: String = red,blue,green
scala> colors.mkString("[", "],[", "]")
res10: String = [red],[blue],[green]</code></pre>
<p>加資料在前面 (prepend):</p>
<pre><code>scala> val colors2 = "yellow" +: colors
colors2: List[String] = List(yellow, red, blue, green)
scala> "yellow" :: colors
res44: List[String] = List(yellow, red, blue, green)</code></pre>
<p>加資料在後面 (append):</p>
<pre><code>scala> val color3 = colors2 :+ "white"
color3: List[String] = List(yellow, red, blue, green, white)</code></pre>
<p>接另一個 List 在前面 (prepend):</p>
<pre><code>scala> val colors4 = colors2 ++: colors
colors4: List[String] = List(yellow, red, blue, green, red, blue, green)
scala> colors2 ::: colors
res45: List[String] = List(yellow, red, blue, green, red, blue, green)</code></pre>
<p>接另一個 List 在後面 (append):</p>
<pre><code>val colors5 = colors2 ++ colors
colors5: List[String] = List(yellow, red, blue, green, red, blue, green)</code></pre>
<h4 id="toc_11">Scala Right Operator</h4>
<ul>
<li>Right Operator <code>:</code>,Scala 定義 Operator,如果結尾是 <code>:</code>,就當作 Right Operator 處理。</li>
<li>一般熟悉的是 Left Operator。eg: <code>a + b</code>,實際是 <code>a.+(b)</code>;Right Operator 是 <code>a +: b</code>,則是 <code>b.+:(a)</code>。</li>
</ul>
<h4 id="toc_12">Scala 慣例:</h4>
<ul>
<li><code>+</code> 用在加單一元素</li>
<li><code>++</code> 用在加一個 collection</li>
<li>只有 <code>mutable</code> 集合有類似 <code>+=</code>, <code>++=</code>, <code>-=</code>, <code>--=</code>。</li>
</ul>
<p>拿掉前兩個:</p>
<pre><code>scala> colors4.drop(2)
res13: List[String] = List(blue, green, red, blue, green)</code></pre>
<p>拿掉最後兩個:</p>
<pre><code>scala> colors4.dropRight(2)
res14: List[String] = List(yellow, red, blue, green, red)</code></pre>
<p>取前兩個:</p>
<pre><code>scala> colors4.take(2)
res0: List[String] = List(yellow, red)</code></pre>
<p>取最後兩個:</p>
<pre><code>scala> colors4.takeRight(2)
res1: List[String] = List(blue, green)</code></pre>
<p>foreach: </p>
<pre><code>scala> colors4 foreach { println }
yellow
red
blue
green
red
blue
green</code></pre>
<p>判斷是否沒有資料:</p>
<pre><code>scala> colors4.isEmpty
res17: Boolean = false
scala> List().isEmpty
res46: Boolean = true</code></pre>
<p>只留符合條件的資料:</p>
<pre><code>scala> colors4 filter { _.length > 4 }
res19: List[String] = List(yellow, green, green)</code></pre>
<p>計算符合條件的資料數量:</p>
<pre><code>scala> colors4 count { _.length > 4 }
res20: Int = 3</code></pre>
<p>去除前面符合條件的資料,當遇到不符合條件就停止 (while - break):</p>
<pre><code>scala> colors4
res47: List[String] = List(yellow, red, blue, green, red, blue, green)
scala> colors4 dropWhile { _.length > 4 }
res21: List[String] = List(red, blue, green, red, blue, green)</code></pre>
<p>取前面符合條件資料,當遇到不符合條件就停止 (while - break):</p>
<pre><code>scala> colors4
res47: List[String] = List(yellow, red, blue, green, red, blue, green)
scala> colors4 takeWhile { _.length > 4 }
res35: List[String] = List(yellow)</code></pre>
<p>收集並加工符合條件的資料 (filter 的加強版) :</p>
<pre><code>scala> colors4 collect { case x if x.length > 4 => x + x }
res48: List[String] = List(yellowyellow, greengreen, greengreen)</code></pre>
<p>找第一個符合條件的資料</p>
<pre><code>scala> val colors4 = List("yellow", "red", "blue", "green", "red")
colors4: List[String] = List(yellow, red, blue, green, red)
scala> colors4.find { _ == "red" }
res0: Option[String] = Some(red)
scala> colors4.find { _ == "black" }
res1: Option[String] = None</code></pre>
<p>找到第一個符合條件的資料,並加工</p>
<pre><code>colors4.collectFirst { case x if (x.length == 3) => x + x }
res3: Option[String] = Some(redred)</code></pre>
<p>找到第一個符合條件的位置。</p>
<pre><code>scala> val colors4 = List("yellow", "red", "blue", "green", "red")
scala> colors4.indexWhere( _ == "red")
res7: Int = 1
scala> colors4.indexWhere( _ == "red", 2)
res8: Int = 4
scala> colors4.indexWhere( _ == "black", 2)
res10: Int = -1
scala> color4.lastIndexWhere( _ == "red")
res11: Int = 4
scala> color4.lastIndexWhere( _ == "red", 3)
res17: Int = 1</code></pre>
<p><strong>由於 Scala 拿掉 <code>break</code>,因此在原本 Java 使用 <code>break;</code> 的情境,就可以使用 <code>dropWhile</code>, <code>takeWhile</code>, <code>collect</code>, <code>collectFirst</code>, <code>indexWhere</code>, <code>lastIndexWhere</code>。</strong></p>
<p><strong>入門 Scala Collection 建議可以由 List 下手。 List 有的 function,大都的 Collection 也都有。</strong></p>
<h3 id="toc_13">ListBuffer</h3>
<p>宣告:</p>
<pre><code>scala> val list = mutable.ListBuffer.empty[String]
list: scala.collection.mutable.ListBuffer[String] = ListBuffer()
scala> val list = mutable.ListBuffer("a", "b", "c")
list: scala.collection.mutable.ListBuffer[String] = ListBuffer(a, b, c)</code></pre>
<p>Append:</p>
<pre><code>scala> list += "d"
res0: list.type = ListBuffer(a, b, c, d)</code></pre>
<p>Prepend:</p>
<pre><code>scala> "e" +=: list
res1: list.type = ListBuffer(e, a, b, c, d)</code></pre>
<h3 id="toc_14">Range</h3>
<pre><code>scala> 1 to 10
res4: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
scala> 1 until 10
res5: scala.collection.immutable.Range = Range(1, 2, 3, 4, 5, 6, 7, 8, 9)</code></pre>
<h3 id="toc_15">Set</h3>
<p>Immutable 及 mutable 都有 Set,以下以 <code>mutable.Set</code> 當例子。</p>
<p>宣告</p>
<pre><code>scala> import scala.collection.mutable
scala> val set = mutable.Set('a', 'b', 'c')
set: scala.collection.mutable.Set[Char] = Set(c, a, b)</code></pre>
<p>資料數:</p>
<pre><code>scala> set.size
res6: Int = 3</code></pre>
<p>加值進 Set:</p>
<pre><code>scala> set += 'd'
res0: set.type = Set(c, d, a, b)</code></pre>
<p>與另一個 Set 合併:</p>
<pre><code>scala> set ++= mutable.Set('a', 'b', 'e', 'f')
res1: set.type = Set(f, c, d, e, a, b)</code></pre>
<p>移除一個值:</p>
<pre><code>scala> set -= 'a'
res5: set.type = Set(f, c, d, e, b)</code></pre>
<p>判斷是否有值:</p>
<pre><code>scala> set('a')
res7: Boolean = false
scala> set('b')
res8: Boolean = true</code></pre>
<h3 id="toc_16">Map</h3>
<p>Immutable 及 mutable 都有 Map,以下以 <code>mutable.HashMap</code> 當例子。</p>
<p>宣告:</p>
<pre><code>scala> import scala.collection.mutable
scala> val map = mutable.HashMap.empty[String, Int]
map: scala.collection.mutable.HashMap[String,Int] = Map()
scala> val map = mutable.HashMap("i" -> 1, "ii" -> 2)
map: scala.collection.mutable.HashMap[String,Int] = Map(ii -> 2, i -> 1)</code></pre>
<p>資料數:</p>
<pre><code>scala> map.size
res0: Int = 2</code></pre>
<p>Keys:</p>
<pre><code>scala> map.keys
res1: Iterable[String] = Set(ii, i)</code></pre>
<p>Key Set:</p>
<pre><code>scala> map.keySet
res2: scala.collection.Set[String] = Set(ii, i)</code></pre>
<p>Values:</p>
<pre><code>scala> map.values
res3: Iterable[Int] = HashMap(2, 1)</code></pre>
<p>Put:</p>
<pre><code>scala> map += ("iii" -> 3)
res0: map.type = Map(ii -> 2, i -> 1, iii -> 3)</code></pre>
<p>Remove:</p>
<pre><code>scala> map -= ("i")
res5: map.type = Map(ii -> 2, iii -> 3)
</code></pre>
<p>直接取值:</p>
<pre><code>scala> map("iii")
res1: Int = 3
scala> map("iiii")
java.util.NoSuchElementException: key not found: iiii
at scala.collection.MapLike$class.default(MapLike.scala:228)
at scala.collection.AbstractMap.default(Map.scala:59)
at scala.collection.mutable.HashMap.apply(HashMap.scala:65)
... 33 elided</code></pre>
<p><strong>直接取值的方式,key 不存在時,會出現 Exception。</strong></p>
<p>使用的時機:你確定一定有值,或者沒值,是很嚴重的錯誤,需要用 Exception 來通知系統。</p>
<p>間接方式:</p>
<pre><code>scala> map.get("iiii")
res3: Option[Int] = None
scala> map.get("iii")
res4: Option[Int] = Some(3)</code></pre>
<p><strong>用 <code>get</code> 的方式,取回 <code>Option</code> 的型別,再做下一步處理,一定沒問題。</strong></p>
<h2 id="toc_17">Compare with Java and Conversions</h2>
<p>Scala 與 Java Collection 互轉的原則:</p>
<pre><code>Iterator <=> java.util.Iterator
Iterator <=> java.util.Enumeration
Iterable <=> java.lang.Iterable
Iterable <=> java.util.Collection
mutable.Buffer <=> java.util.List
mutable.Set <=> java.util.Set
mutable.Map <=> java.util.Map
mutable.ConcurrentMap <=> java.util.concurrent.ConcurrentMap
</code></pre>
<p>使用 Scala 大都會用到 Java 既有的 Library,所以會遇到取得的資料型別是 Java 的 Collection。如果要使用 Scala 提供的功能時,會需要將 Java 的 Collection 轉成 Scala 的 Collection。</p>
<p>遇到這種情形時,只要 import JavaConversions 即可,程式碼:<code>import scala.collection.JavaConversions._</code>。</p>
<p><strong>注意:</strong></p>
<ul>
<li>這類型的轉換,在 Scala 是利用 <code>implicit</code> 機制完成,原理是產生新的 Scala Collection Class 去 wrap 原來的 Java Class,因此在使用時要小心,以免吃光記憶體。</li>
<li>在 Eclipse 上,會提醒 <code>implicit</code> 的轉換。</li>
<li>依 Java 習慣,不建議直接用 <code>import scala.collection.JavaConversions._</code>,可以利用 Eclipse 的 <strong>Source</strong> -> <strong>Organize Imports</strong> 來整理 import,會自動整理成明確 import 那些東西。</li>
</ul>
<script type="text/x-mathjax-config">
if (typeof MathJaxListener !== 'undefined') {
MathJax.Hub.Register.StartupHook('End', function () {
MathJaxListener.invokeCallbackForKey_('End');
});
}
</script>
<script type="text/javascript" src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
</div>kigihttp://www.blogger.com/profile/09881273562560954871noreply@blogger.com0tag:blogger.com,1999:blog-6312720754439261813.post-74383449454693834102015-07-26T10:10:00.003+08:002015-07-26T10:10:43.782+08:00Scala 的物件導向 (Session 3)<div class='mymarkdown'>
<h1 id="toc_0">Scala 的物件導向</h1>
<h2 id="toc_1">OOP</h2>
<ul>
<li>封裝 (Encapsulation)</li>
<li>繼承 (Inheritance)</li>
<li>多型 (Polymorphism)</li>
</ul>
<h2 id="toc_2">Class</h2>
<h3 id="toc_3">宣告方式</h3>
<pre><code>class 名稱 [extends Class or Trait 名稱] [with Trait 名稱] {
}</code></pre>
<p>eg:</p>
<pre><code>scala> class Test
defined class Test
scala> class Test2 extends Test
defined class Test2
</code></pre>
<pre><code>class 名稱([存取等級] [val or var] 變數名稱: 型別) [extends Class or Trait 名稱] [with Trait 名稱] [with Trait 名稱] {
def this() = this(...)
}
</code></pre>
<p>eg:</p>
<pre><code>scala> class Test(a: Int, b: Double) {
| println(s"${a}, ${b}")
| }
defined class Test
scala> class Test2(a: Int) extends Test(a, 10.0) {
| def this() = this(20)
| }
defined class Test2
</code></pre>
<p><strong>Java Bean Like</strong></p>
<pre><code>scala> class Test(var name: String, var age: Int, var address: String)
scala> val a = new Test("abc", 10, "aaaaaa")
a: Test = Test@74d1dd7e
scala> a.name
res0: String = abc
scala> a.age
res1: Int = 10
scala> a.address
res2: String = aaaaaa
scala> a.name = "abcdef"
a.name: String = abcdef
scala> a.age = 20
a.age: Int = 20
scala> a.address = "bbbbbb"
a.address: String = bbbbbb
</code></pre>
<h3 id="toc_4">Primary and Auxiliary Contructors</h3>
<p>在 Scala 及 Swift 中,Constructor 有主、副之分。</p>
<p>eg:</p>
<pre><code>scala> class Test(a: Int, b: Double) {
| println(s"${a}, ${b}")
|
| def this() = this(10, 20.0)
| }
defined class Test
</code></pre>
<p><strong>Primary Constructor</strong> 是 </p>
<pre><code>class Test(a: Int, b: Double) {
println(s"${a}, ${b}")
}
</code></pre>
<p><strong>Auxiliary Constructor</strong> 是</p>
<pre><code> def this() = this(10, 20.0)</code></pre>
<p>轉換成 Java 語法來看:</p>
<pre><code>class Test {
public Test(int a, double b) {
System.out.println("" + a + ", " + b)
}
public Test() {
this(10, 20.0);
}
}
</code></pre>
<ul>
<li><code>public Test(int a, double b)</code> 是 <strong>Primary Constructor</strong></li>
<li><code>public Test()</code> 是 <strong>Auxiliary Constructor</strong></li>
</ul>
<h3 id="toc_5">Override</h3>
<p>當子類別要改寫父類別的函式時,在宣告函式時,一定要用 <code>override</code>,否則會報錯。 </p>
<p>eg:</p>
<pre><code>scala> class Test {
| def echo = "Echo"
| }
defined class Test
</code></pre>
<p><strong>沒用 <code>override</code> 會報錯</strong></p>
<pre><code>scala> class Test2 extends Test {
| def echo = "Echo2"
| }
<console>:9: error: overriding method echo in class Test of type => String;
method echo needs `override' modifier
def echo = "Echo2"</code></pre>
<p><strong>正解</strong></p>
<pre><code>scala> class Test2 extends Test {
| override def echo = "Echo2"
| }</code></pre>
<h3 id="toc_6">注意事項</h3>
<p>宣告 class 時,如果 primary constructor 的變數,沒有加 <code>val</code> or <code>var</code> 則不會自動變成 member data。</p>
<pre><code>scala> class Test(a: Int)
defined class Test
scala> val t = new Test(10)
t: Test = Test@7c8c0c57
scala> t.a
<console>:10: error: value a is not a member of Test
t.a</code></pre>
<h2 id="toc_7">Object</h2>
<p><strong>Scala 的 <code>object</code> 等同於 Java Singleton</strong></p>
<p>Scala 允許 <code>object</code> 的名稱與 <code>class</code> 相同,且可以放在同一份 source code 中。這類的 <code>object</code> 稱作 <strong>Companion Object</strong>。</p>
<p>類比 Java 的實作,就是把 <strong><code>static</code></strong> 相關的變數及函式(Class member data and function),放到 <code>object</code> 中。</p>
<p>在 <code>object</code> 中,就不用 <strong><code>static</code></strong> 這個關鍵字。</p>
<p>eg:</p>
<pre><code>scala> object Test {
| def myTest = println("abc")
| }
defined object Test
scala> Test.myTest
abc
</code></pre>
<p>在 Java 要寫一個應用程式 (Application),需要在一個 <code>public class</code> 內,宣告一個 <code>public static void main (String[] args)</code> 函式。</p>
<p>eg:</p>
<pre><code>public class Test {
public static void main(String[] args) {
}
}</code></pre>
<p>改用 Scala 後,則在一個 <code>object</code> 宣告 <code>def main(args: Array[String])</code></p>
<p>eg:</p>
<pre><code>object Test {
def main(args: Array[String]) {
}
}</code></pre>
<h2 id="toc_8">Trait</h2>
<p>Scala 的 <code>trait</code> 可以類比成 Java 的 <code>interface</code>。一個 <code>class</code> 可以繼承(實作)多個 <code>trait</code>。在使用時,如果 <code>class</code> 沒有繼承其他的 <code>class</code> or <code>trait</code> 時,則用 <code>extends</code>,否則用 <code>with</code></p>
<p>eg:</p>
<pre><code>scala> trait MyTrait
defined trait MyTrait
scala> class Test
defined class Test
scala> class Test2 extends Test with MyTrait
defined class Test2
scala> class Test3 extends MyTrait
defined class Test3
</code></pre>
<p>在 Java 8 以前,<code>interface</code> 沒有 default function 的功能,也因此無法在 <code>interface</code> 內實作函式。</p>
<p>Scala 的 <code>trait</code> 則打破這個限制,允許在 <code>trait</code> 內有變數、函式的實作。也因此更起達到多重繼承的效果。</p>
<p>eg: Trait 內含變數與實作函式</p>
<pre><code>scala> trait MyTrait {
| val a = 10
| def test: Int
| def sum(x: Int, y: Int) = x + y + a
| }
defined trait MyTrait
scala> class Test extends MyTrait {
| def test = a + 1000
| }
defined class Test
scala> val t = new Test
t: Test = Test@597b40a8
scala> t.test
res0: Int = 1010
scala> t.sum(t.a, 10)
res2: Int = 30</code></pre>
<p>eg: 多個 Trait;有多重繼承效果</p>
<pre><code>scala> trait MyTrait1 {
| val a = 10
| def test1: Int
| }
defined trait MyTrait1
scala> trait MyTrait2 {
| val b = 20
| def test2: Int
| }
defined trait MyTrait2
scala> trait MyTrait3 {
| val c = 30
| def test3: Int
| }
defined trait MyTrait3
scala> class Test extends MyTrait1 with MyTrait2 with MyTrait3 {
| def test1 = b + 1
| def test2 = c + 2
| def test3 = a + 3
| }
defined class Test
scala> val t = new Test
t: Test = Test@37f7bfb6
scala> t.test1
res0: Int = 21
scala> t.test2
res1: Int = 32
scala> t.test3
res2: Int = 13</code></pre>
<p>object 也可以繼承(實作) trait</p>
<pre><code>scala> trait MyTrait
defined trait MyTrait
scala> object Test extends MyTrait
defined object Test</code></pre>
<h2 id="toc_9">Scala 增強的功能</h2>
<h3 id="toc_10">自定 Access Level</h3>
<p>Scala 允許自定 Access Level,因此在程式設計上會更有彈性,安全性也會更高。</p>
<p>使用方式:<code>modifier[this, class or package]</code>. eg: <code>private[Test]</code></p>
<p>eg: 變數只允許自己本身的 instance 使用。比 private 更嚴格。</p>
<pre><code>scala> class TestPrivateThis {
| private[this] val a = 10
|
| def func(): Int = a + 10
|
| def func(that: TestPrivateThis): Int = a + that.a
| }
<console>:12: error: value a is not a member of TestPrivateThis
def func(that: TestPrivateThis): Int = a + that.a</code></pre>
<h3 id="toc_11">Operator Overloading</h3>
<p>在 Java 中,無法在 class 中,定義 <code>+</code>,<code>-</code>,<code>*</code>,<code>/</code> 這類四則運算。但在 Scala 則可以,這會讓程式碼更簡潔也更方便閱讀。</p>
<p>eg:</p>
<pre><code>class Rational(n: Int, d: Int) {
require(d != 0)
def this(n: Int) = this(n, 1)
def gcd(a: Int, b: Int): Int = if (a == 0) b else gcd(b % a, a)
val g = gcd(n, d)
val numer = n / g
val denom = d / g
override def toString = if (denom != 1) s"${numer} / ${denom}" else s"${numer}"
def +(that: Rational) = new Rational(numer * that.denom + denom * that.numer, denom * that.denom)
def -(that: Rational) = new Rational(numer * that.denom - denom * that.numer, denom * that.denom)
def *(that: Rational) = new Rational(numer * that.numer, denom * that.denom)
def /(that: Rational) = new Rational(numer * that.denom, denom * that.numer)
}
object Rational {
def main(args: Array[String]) {
val r1 = new Rational(3, 7)
val r2 = new Rational(5, 21)
println(r1 * r2)
}
}</code></pre>
<h3 id="toc_12">apply Function</h3>
<p>當在 class 宣告 <code>apply</code> 的函式後,使用時,可以省略函式的名稱。</p>
<p>eg:</p>
<pre><code>scala> class Test {
| def apply(a: Int, b: Int): Int = a + b
| }
defined class Test
scala> val t = new Test
t: Test = Test@300a2ae
scala> val a = t(10, 20)
a: Int = 30
</code></pre>
<p>在上例中 <code>val a = t(10, 20)</code> 就是呼叫 <code>apply</code> 函式。<code>apply</code> 函式,在 scala collection 相關 class (HashMap, Array 等) 很常用到。</p>
<h4 id="toc_13">case class</h4>
<p>Scala 在宣告 <code>class</code> 時,可以使用 <code>case</code> 這個修飾詞,使用後,在產生 instance (以後就不用 object 這個字眼,以免跟 scala 的 <code>object</code> 混餚) 時,可以省略 <code>new</code>;如果 primary contructor 有參數時,會自動將參數轉成 <strong>val</strong> 型態的 member data。</p>
<p>eg:</p>
<pre><code>scala> case class Test(name: String, age: Int)
defined class Test
scala> val t = Test("abc", 20)
t: Test = Test(abc,20)
scala> t.name
res0: String = abc
scala> t.age
res1: Int = 20
scala> t.name = "abcdef"
<console>:10: error: reassignment to val
t.name = "abcdef"</code></pre>
<p><code>case class</code> 與 <code>apply</code>, <code>object</code>, pattern match (<code>match</code> - <code>case</code>) 有密不可分的關係,在第二階段會再詳述。</p>
<h2 id="toc_14">Enumeration</h2>
<h3 id="toc_15">基本用法</h3>
<h4 id="toc_16">Java 版本</h4>
<pre><code>public enum DirectionJava {
TOP, DOWN, LEFT, RIGHT;
}</code></pre>
<p>在 Java 使用 <code>enum</code> 宣告即可。 </p>
<p>Java class 使用 <code>enum</code> 後會變成 final class,因此 enum 不能再繼承其他的 class (Java 是單一繼承),也不能再被其他 class 繼承。</p>
<p><code>enum</code> 的 Constructor 設計成 <code>private</code>,所以之後也無法自行產生 instance,只能使用內定的值 (static 值) 或 null。</p>
<p>總結以上,各位不覺得 <code>enum</code> 就跟 signleton 很像嗎?!</p>
<h4 id="toc_17">Scala 版本</h4>
<pre><code>object DirectionScala extends Enumeration {
val Top, Down, Left, Right = Value
}</code></pre>
<p>Scala 的 Enumation 歷史比 Java 早,因此實作語法上,與 Java 不太相同,但觀念差不多。</p>
<p>Scala 的 Enumeration 跟 Java 的很類似,但 Scala 的內定值型別是 Enumeration.Value。 </p>
<p>Enumeration.Value 本身是個 abstract class。在 Enumeration 內有一個實作的 class: <code>protected class Val</code>,如果需要自定 Enumeration 時,就會需要繼承這個 class。</p>
<h3 id="toc_18">進階</h3>
<h4 id="toc_19">Java 版</h4>
<pre><code>public enum Planet {
MERCURY (3.303e+23, 2.4397e6),
VENUS (4.869e+24, 6.0518e6),
EARTH (5.976e+24, 6.37814e6),
MARS (6.421e+23, 3.3972e6),
JUPITER (1.9e+27, 7.1492e7),
SATURN (5.688e+26, 6.0268e7),
URANUS (8.686e+25, 2.5559e7),
NEPTUNE (1.024e+26, 2.4746e7);
private final double mass; // in kilograms
private final double radius; // in meters
Planet(double mass, double radius) {
this.mass = mass;
this.radius = radius;
}
private double mass() { return mass; }
private double radius() { return radius; }
// universal gravitational constant (m3 kg-1 s-2)
public static final double G = 6.67300E-11;
double surfaceGravity() {
return G * mass / (radius * radius);
}
double surfaceWeight(double otherMass) {
return otherMass * surfaceGravity();
}
public static void main(String[] args) {
if (args.length != 1) {
System.err.println("Usage: java Planet <earth_weight>");
System.exit(-1);
}
double earthWeight = Double.parseDouble(args[0]);
double mass = earthWeight/EARTH.surfaceGravity();
for (Planet p : Planet.values())
System.out.printf("Your weight on %s is %f%n",
p, p.surfaceWeight(mass));
}
}</code></pre>
<h4 id="toc_20">Scala 版</h4>
<pre><code>object Planets extends Enumeration {
val G: Double = 6.67300E-11
final case class Planet private[Planets] (mass: Double, radius: Double) extends Val {
def surfaceGravity(): Double = G * mass / (radius * radius)
def surfaceWeight(otherMass: Double): Double = otherMass * surfaceGravity()
}
val Mercury = Planet(3.303e+23, 2.4397e6)
val Venus = Planet(4.869e+24, 6.0518e6)
val Earth = Planet(5.976e+24, 6.37814e6)
val Mars = Planet(6.421e+23, 3.3972e6)
val Jupiter = Planet(1.9e+27, 7.1492e7)
val Saturn = Planet(5.688e+26, 6.0268e7)
val Uranus = Planet(8.686e+25, 2.5559e7)
val Neptune = Planet(1.024e+26, 2.4746e7)
def main(args: Array[String]) {
require(args.length == 1, "Usage: java Planet <earth_weight>")
val earthWeight = args(0).toDouble
val mass = earthWeight / Planets.Earth.surfaceGravity
Planets.values.foreach(p => println("Your weight on %s is %f%n".format(p, p.asInstanceOf[Planet].surfaceWeight(mass))))
}
}
</code></pre>
<h2 id="toc_21">附錄</h2>
<h3 id="toc_22">activator</h3>
<h4 id="toc_23">產生專案</h4>
<p>完整的指令: <code>activator new [project-name] [template-name]</code></p>
<p>一般的步驟:</p>
<ul>
<li>執行:<code>activator new 專案名稱</code></li>
<li>選擇專案的 template</li>
</ul>
<pre><code>Fetching the latest list of templates...
Browse the list of templates: http://typesafe.com/activator/templates
Choose from these featured templates or enter a template name:
1) minimal-akka-java-seed
2) minimal-akka-scala-seed
3) minimal-java
4) minimal-scala
5) play-java
6) play-scala
(hit tab to see a list of all templates)
</code></pre>
<p>1), 2) 是 akka 專案</p>
<p>3), 4) 是一般應用程式專案</p>
<p>5), 6) 是寫 Play Framework (web) 專案</p>
<ul>
<li>在專案目錄下的 <code>project</code> 目錄,加入 <code>plugins.sbt</code> 檔案,內容如下:</li>
</ul>
<pre><code>addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "4.0.0")
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.13.0")</code></pre>
<p><strong>注意:兩行中間,一定要空一行</strong></p>
<ul>
<li>執行:<code>activator eclipse</code> 產生 eclipse 可以匯入的專案格式</li>
<li>使用 eclipse 匯入 (General -> Existing Projects into Workspace)</li>
</ul>
<h4 id="toc_24">命令列模式</h4>
<p>在專案的目錄下,執行 <code>activator</code>,會進入 Activator 的命令列模式。與 Maven 相同,有 <code>reload</code>, <code>update</code>,<code>clean</code>, <code>compile</code>, <code>package</code>, <code>test</code> 等指定;如果有加入 sbteclipse-plugin,也可在這使用 <code>eclipse</code>。</p>
<ul>
<li>reload: 重新載入 SBT 設定</li>
<li>update: 更新相關的 dependency library</li>
<li>clean: 將先前產生的 *.class, *.jar 清除</li>
<li>compile: 編譯專案</li>
<li>package: 打包專案</li>
<li>test: 執行 Unit Test</li>
</ul>
<h4 id="toc_25">修改 Dependency</h4>
<p>與 Maven 相同, Activator 可以管理專案的 dependency。修改專案目錄下的 <code>build.sbt</code>。</p>
<p>eg:</p>
<pre><code>libraryDependencies += "mysql" % "mysql-connector-java" % "5.1.34"
libraryDependencies += "org.apache.commons" % "commons-lang3" % "3.3.2"
libraryDependencies += "org.apache.commons" % "commons-email" % "1.3.3"</code></pre>
<p><strong>注意:每行中間,一定要空一行</strong></p>
<p>修改完 dependency,請再執行 <code>activator eclipse</code> 重新產生 eclipse 的專案,在 eclipse IDE 再重新 reload 一次,即可生效。</p>
<script type="text/x-mathjax-config">
if (typeof MathJaxListener !== 'undefined') {
MathJax.Hub.Register.StartupHook('End', function () {
MathJaxListener.invokeCallbackForKey_('End');
});
}
</script>
<script type="text/javascript" src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
</div>kigihttp://www.blogger.com/profile/09881273562560954871noreply@blogger.com0tag:blogger.com,1999:blog-6312720754439261813.post-2498074675400082892015-07-25T23:02:00.000+08:002015-07-25T23:02:02.252+08:00基本語法與 Data Type (Session 2)<div class='mymarkdown'>
<h1 id="toc_0">基本語法與 Data Type</h1>
<h2 id="toc_1">前言</h2>
<ul>
<li>Scala 的 <code>Tab</code> 是<strong>兩個</strong>空隔,與 Java 預設四個不同</li>
<li>Scala 已經沒有 <code>break</code> 與 <code>continue</code> 關鍵字可用</li>
<li>Scala 還保有 <code>return</code> 但不建議使用</li>
<li>Scala 還保有 <code>throw</code> Exception,但不建議使用。建議 refactor 成 Option or Either;IO相關可例外。</li>
<li>Scala 依然可以用 <code>try</code>-<code>catch</code>-<code>finally</code>。</li>
</ul>
<h2 id="toc_2">變數宣告方式</h2>
<p>變數的宣告方式如下:</p>
<pre><code>var 變數名稱: 資料型別 = 值
val 變數名稱: 資料型別 = 值</code></pre>
<p>ex:</p>
<pre><code>val a: Int = 10
var b: Double = 20.0
</code></pre>
<p>其中,資料型別可以省略,如下:</p>
<pre><code>val a = 10
var b = 20.0</code></pre>
<p><code>val</code> 等同在 java 使用 <code>final</code>。</p>
<p><code>var</code> 則如同 java 一般的宣告。</p>
<p>scala 鼓勵儘量使用 <code>val</code>。 </p>
<p>使用 <code>var</code> 宣告時,可以使用 <code>_</code> (萬用字元) 當預設值,此時 scala 會依型別,給予預設值。<code>val</code> 則不可用 <code>_</code>。</p>
<pre><code>var a: Int = _ /* a 會是 0 */
class Test
var b: Test = _ /* b 會是 null */</code></pre>
<h2 id="toc_3">Java Primitive Type</h2>
<p>Java primitive type are</p>
<ul>
<li>byte</li>
<li>char</li>
<li>short</li>
<li>int</li>
<li>long</li>
<li>float</li>
<li>double</li>
<li>boolean</li>
</ul>
<p>以上這些在 Scala 是被轉成所謂的 <code>Value Class</code> 全都繼承自 <code>AnyVal</code>。</p>
<p>Scala 定義的 <code>Value Class</code> 有:</p>
<ul>
<li>Unit -> ()</li>
<li>Byte -> byte</li>
<li>Char -> char</li>
<li>Short -> short</li>
<li>Int -> int</li>
<li>Long -> long</li>
<li>Float -> float</li>
<li>Double -> double</li>
<li>Boolean -> boolean</li>
</ul>
<p><strong>注意:省略型別時,整數值會預設是 <code>Int</code>,浮點數值會是 <code>Double</code>。</strong></p>
<h2 id="toc_4">String</h2>
<p>Scala 的 String <strong>等於</strong> Java 的 String。唯一要注意的是 <code>.equals</code>。在 Java 比對字串的內容時,要使用 <code>.equals</code> 不能使用 <code>==</code>。但在 Scala 則可以。</p>
<p><strong>In Java</strong></p>
<pre><code>String str = "hello world!";
"hello".equals(str);
</code></pre>
<p><strong>In Scala</strong></p>
<pre><code>val str = "Hello world!"
"hello" == str
</code></pre>
<h3 id="toc_5">Scala 的字串有兩個方便的功能,讓寫作程式更方便</h3>
<ul>
<li>String Interpolation:字串與變數結合,字串的開頭為<code>s</code>, 可使用 <code>$</code> 來將變數加入字串中,或者用 <code>${}</code>將 statement 加入。讓程式寫作更方便,可讀性也變高。</li>
</ul>
<pre><code>val a = 10
val b = 20
println(s"$a + $b = ${a + b}")</code></pre>
<ul>
<li>Raw String Delimiter:在 Scala 中,可以使用 <code>"""</code>,不處理脫序字元,且可多行。這項功能在宣告正規表示式很好用。</li>
</ul>
<pre><code>/* Java 列印 \r\n */
System.out.println("\\r\\n");
/* Scala */
println("""\r\n""")
/* 多行字串 */
val a = """abc
def
ccc
eee
"""
println(a)
/* Scala Regex */
val a = """\d+""".r</code></pre>
<h2 id="toc_6">Option</h2>
<p><code>Option</code> 是用來取代 <code>null</code>。<code>Option</code> 有兩個 subclass:</p>
<ul>
<li>Some</li>
<li>None</li>
</ul>
<p>宣告時用 generic 方式,來指定回傳的型別。</p>
<p>從<code>Option</code> 取值前,先用 <code>isDefined</code> 來判斷是否是 <code>Some</code>,是的話,再用 <code>get</code> 來取值。</p>
<pre><code>def parseInt(str: String): Option[Int] = {
try {
Option(str.toInt)
}
catch {
case ex: Exception => None
}
}
val a = parseInt("abc")
if (a.isDefined) println(a.get) else println("None")
</code></pre>
<p>在使用 Functional Languge 方式時寫程式時,不會這樣子做。</p>
<h2 id="toc_7">Tuple</h2>
<p>Tuple 可以將兩種以上,不同型別的資料組合起來使用;可以把它當作更精簡的 Bean 來看。</p>
<p>宣告:</p>
<pre><code> val a: (Int, String) = (10, "ABC")</code></pre>
<p>或省略型別</p>
<pre><code>val a = (10, "ABC")</code></pre>
<p>取值使用時,從 <strong>_1</strong> 開始,指定第一個值。以此類推。</p>
<pre><code>val a = (10, "ABC")
println(a._2)</code></pre>
<p><strong>Tuple 最多只可以用到 22 個參數。</strong></p>
<h2 id="toc_8">Function</h2>
<p>完整的 function 宣告如下:</p>
<pre><code>def 函數名稱(變數名稱: 型別, 變數名稱: 型別, ...): 回傳型別 = {
函數內容
}
/* 等同 Java:
boolean test(int a, int b) {
return a == b;
} */
def test(a: Int, b: Int): Boolean = {
a == b
}
/* 等同 Java:
void test(int a) {
System.out.println(a);
} */
def print(a: Int): Unit = {
println(a)
}</code></pre>
<p><strong>函式的回傳值,是看函式的最後一個 statement 的回傳值決定</strong></p>
<p>簡潔的寫法:</p>
<ul>
<li>沒有回傳值時</li>
</ul>
<pre><code>原:
def test(a: Int): Unit = {
}
簡:
def test(a: Int) {
}</code></pre>
<ul>
<li>省略寫回傳值,依函式的最後一行來決定</li>
</ul>
<pre><code>原:
def test(a: Int): Int = {
val b = a + 10
b
}
簡:
def test(a: Int) = {
val b = a + 10
b
}</code></pre>
<ul>
<li>沒有傳入參數時,可以省略 <code>()</code>,呼叫時,也可以省略。</li>
</ul>
<pre><code>原:
def test(): Int = {
val a = 10
a
}
簡:
def test: Int = {
val a = 10
a
}
再簡:
def test = {
val a = 10
a
}
呼叫時:
test</code></pre>
<ul>
<li>如果函式只有一行 statement,<code>{</code> 和 <code>}</code> 可省略</li>
</ul>
<pre><code>原:
class Bean {
private var age: Int = 0
def getAge(): Int = {
age
}
def setAge(a: Int): Unit = {
age = a
}
}
簡:
class Bean {
private var age = 0
def getAge = age
def setAge(a: Int) = age = a
}
使用:
val b = new Bean
b.setAge(10)
b.getAge</code></pre>
<h3 id="toc_9">Function 進階</h3>
<ul>
<li>傳入的參數,預設都是 <code>val</code>,也就是不能再修改值。</li>
</ul>
<pre><code>def test(a: Int) = {
a = 10 /* error: reassignment to val */
}</code></pre>
<ul>
<li>Named Arguments:在呼叫函式時,可以加入函式宣告的參數名稱,尤其是當函式的參數很多時,來增加可讀性;有指定參數的名稱時,就不一定要依照函式參數的順序傳入。</li>
</ul>
<pre><code>def test(a: Int, b: Int) = a + b
/* a,b 的順序就可互換 */
test(b = 10, a = 30)</code></pre>
<ul>
<li>Default Parameter Value:宣告函式時,可以給定參數預設值,當呼叫時,沒有傳入值時,就會使用此預設值。這項功能,常見於動態語言, ex: PHP。在設計程式,或者做 refactor 時,可利用這項特性,來增強相容性。</li>
</ul>
<pre><code>def test(a: Int, b: Int = 0) = a + b
test(10) /* b 會用 0 傳入 */
test(10, 20)</code></pre>
<p>refactor 案例:</p>
<pre><code>原始:
def test(a: Int): Int = a + 10</code></pre>
<p>後來希望可以控制 10 這個值,函式改寫成:</p>
<pre><code>def test(a: Int, b:Int) = a + b</code></pre>
<p>但已經存在的程式碼,太多地方使用,不想去更動它,所以可以寫成:</p>
<pre><code>def test(a: Int, b: Int = 10) = a + b</code></pre>
<p>Java 也行。但要多一個函式的宣告:</p>
<pre><code>原:
int test(int a) {
return a + 10;
}
改:
int test(int a, int b) {
return a + b;
}
int test(int a) {
return test(a, 10);
}</code></pre>
<p><strong>有預設值的參數,不一定是要放在最後,但建議放在最後。目前我遇到有設計這項功能的程式語言,都會限定要放在最後。</strong></p>
<pre><code>/* 沒有放在最後的話,就要給定參數名稱 */
def test(a: Int, b: Int = 0, c: Int) = a + b + c
test(10, 20) /* error: not enough arguments for method test */
test(a = 10, c = 20)</code></pre>
<ul>
<li>Repeapted arguments: 可以宣告不定個數的參數的函式,並且可以使用 Array 或 List 傳入。</li>
</ul>
<pre><code>def test(args: String*) {
args.foreach { println }
}
test("1", "2")
test("A", "B", "C")
val args = Array("a", "b", "c", "d")
test(args: _*)
val lst = List("a", "1", "b", "2")
test(lst: _*)</code></pre>
<h2 id="toc_10">IF - ELSE IF - ELSE</h2>
<p>在 Scala 拿掉了 Java 的三元運算子 <code>?:</code> (ex: w = x ? y : z);需改用 <code>if else</code></p>
<pre><code>val w = if (x) y else z</code></pre>
<p>也從這個例子中,Scala 在 <code>if - else</code> 的設計,是可以回傳值,在程式寫作時,可以多利用,ex:</p>
<pre><code>/* Java */
int a = 0;
if (xx)
a = 10;
else if (yy)
a = 20;
else
a = 30;
或者
int a = xx ? 10 : (yy ? 20 : 30);
/* Scala */
val a = if (xx) 10 else if (yy) 20 else 30</code></pre>
<h2 id="toc_11">While Loop</h2>
<p><code>while</code> 的使用方式,跟 java 一樣</p>
<pre><code>def gcdLoop(x: Long, y: Long): Long = {
var a = x
var b = y
while (a != 0) {
val temp = a
a = b%a
b = temp
}
b
}
var line = ""
do {
line = readLine()
println("Read: "+ line)
} while (line != "")</code></pre>
<h2 id="toc_12">For Loop</h2>
<p>Scala 的 <code>for</code> 語法,跟 Java 的 <code>for</code> 用在 <code>Iterable</code> 很像。</p>
<pre><code>/* 列印 1 ~ 9 */
for (i <- 1 to 9)
println(i)
for (i <- 1 until 10) {
println(i)
}
/* 九九乘法 */
for (i <- 1 to 9) {
for (j <- 1 to 9)
println(s"$i x $j = ${i + j}")
}</code></pre>
<p>可以在 <code>for</code> 加 <code>if</code> 判斷,多個 <code>if</code> 時,是用 <code>and</code> 來判斷</p>
<pre><code>/* 列印 1 ~ 100 的偶數 */
for (i <- 1 to 100 if i % 2 == 0)
println(i)
/* 多個判斷 */
for (i <- 1 to 100
if i % 2 == 0
if i % 5 == 0
) println(i)</code></pre>
<p>也可以將 nested for 縮成一個 for。使用時,要用 <code>;</code> 來區分 nested for</p>
<pre><code>/* 九九乘法 */
for (i <- 1 to 9;
j <- 1 to 9
) println(s"$i x $j = ${i * j}")</code></pre>
<p>使用 <code>for yield</code> 組出 collection</p>
<pre><code>val a = for (i <- 1 to 9) yield i
val b = for (i <- 1 to 9) yield {
if (i % 2 == 0) i
else 0
}
/* 可以把 else 0 拿掉試看看 */</code></pre>
<p>Scala 的 <code>for</code> 雖然語法上跟 Java 很像,但底層的實作卻是大不相同。 Scala 的 <code>for</code> 主要是依循 Functional Language 的 <strong>Monads</strong> 模型做出來的。因此在使用 <code>for yield</code> 時,要去注意回傳 collection 的型別是什麼。</p>
<p><strong>當轉換到 Functional Language 的思維時,其實 <code>for</code> 就會很少使用。都會直接用 monads 的模型在實作。</strong></p>
<h2 id="toc_13">MATHCH CASE</h2>
<p><code>match case</code> 是 Scala 最重要的功能之一,使用的概念與 Java 的<code>switch case</code> 類似,但有更強大的功能。</p>
<p>由於 Scala 拿掉了 <code>break</code>,所以在每個 <code>case</code> 執行完後,會直接離開 <code>match</code> block。這個與 Java <code>switch case</code> 有很大的差別;在 Java 的 <code>case</code> 如果沒有下 <code>break</code>,會往下一個 <code>case</code> 繼續執行。</p>
<p>Scala 的 default case 的寫法是: <code>case _</code>,使用萬用字元 <code>_</code> 當 default case。</p>
<pre><code> val a = 10
a match {
case 1 =>
println(1)
case 2 =>
println(2)
case _ =>
println("other")
}
val b = "abc"
b match {
case "def" =>
println("DEF")
case "abc" =>
println("ABC")
case _ =>
println(b)
}</code></pre>
<p>進階用法:</p>
<ul>
<li>case 加入型別及 <code>if</code> 的判斷</li>
</ul>
<pre><code>val a = 10
a match {
case x if (x % 2 == 0) =>
println("event")
case _ =>
println("odd")
}</code></pre>
<ul>
<li><code>match case</code> 可以回傳值</li>
</ul>
<pre><code>val a = 99
val isEvent = a match {
case x if (a % 2 == 0) => true
case _ => false
}</code></pre>
<p>在 Scala 使用 <code>match case</code> 時,雖然不強制一定要給 default case,但還是
養成好習慣,寫入 default case 要做什麼處理,當然也可以不做任何事情。</p>
<p>Scala 如果比對不到時,會出現 Runtime Error。</p>
<pre><code>val a = 5
a match {
case a < 5 =>
println("a < 5")
case _ => /* 不做任何處理 */
}</code></pre>
<p><strong>不同的程式語言對於 match(switch) case 設計不盡相同,像 C++, Java 需要下 break 才會離開 switch block。Scala 與 Apple Swift 則不用。</strong></p>
<p><strong>推薦各位學 Scala 還有一項優點,就是熟 Scala 後,想再去學 Apple Swift 會非常快。Swift 的語法與設計概念,有很多與 Scala 雷同,而且比 Scala 更好用。</strong></p>
<h2 id="toc_14">TRY - CATCH - FINALLY</h2>
<p>scala 還保留 <code>try</code>,<code>catch</code>,<code>finally</code>,使用觀念與注意事項與 Java相同。</p>
<ul>
<li><code>finally</code> 是最後一定會被執行的區塊,<strong>千萬不要</strong>在此用 <code>return</code> 回傳值。<strong>切記:Scala 禁用 <code>return</code>。</strong></li>
<li><code>catch</code> 的語法,有點像 <code>match case</code>,變成 <code>catch case</code>。</li>
<li>與 Java 不同的地方,就是 <code>try - catch</code> 可以回傳值</li>
</ul>
<p>語法示意:</p>
<pre><code>try {
.....
}
catch {
case ex: IOException =>
...
case ex: SQLException =>
...
case ex: Exception =>
...
}
finally {
....
}</code></pre>
<p>ex: </p>
<pre><code>/* 注意 try catch 是可以回傳值的 */
def parseInt(a: String) = {
try {
Option(a.toInt)
}
catch {
case ex: Exception => None
}
finally {
println("end")
}
}</code></pre>
<p><strong>再次提醒,不論 Java 或 Scala 都千萬不要在 <code>finally</code> 用 <code>return</code>。</strong></p>
<pre><code>def f: Int = try { return 1 } finally { return 2 }
def g: Int = try { 1 } finally { 2 }
f /* return 2 */
g /* return 1 */</code></pre>
<script type="text/x-mathjax-config">
if (typeof MathJaxListener !== 'undefined') {
MathJax.Hub.Register.StartupHook('End', function () {
MathJaxListener.invokeCallbackForKey_('End');
});
}
</script>
<script type="text/javascript" src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
</div>kigihttp://www.blogger.com/profile/09881273562560954871noreply@blogger.com0tag:blogger.com,1999:blog-6312720754439261813.post-17275079634911120552015-07-25T22:42:00.003+08:002015-07-25T22:44:34.537+08:00由 Java 跨入 Scala 要注意的事項 (Session 1)<div class='mymarkdown'>
<h1 id="toc_0">由 Java 跨入 Scala 要注意的事項</h1>
<h2 id="toc_1">Java 開發者需注意事項</h2>
<h3 id="toc_2">分號 <code>;</code></h3>
<p>Java 一定要使用 <code>;</code> 隔開 statement;在 Scala 中,除非同一行有兩個以上的 statement,才需要用 <code>;</code> 隔開,否則可<strong>省略</strong>。</p>
<p><strong>In Java</strong></p>
<pre><code>int a = 10;
int b = 20;
a = 20; b = 30;
</code></pre>
<p><strong>In Scala</strong></p>
<pre><code>var a = 10
var b = 20
a = 20; b = 30
</code></pre>
<h3 id="toc_3">宣告時,型別可以省略</h3>
<p>在宣告變數了,可以省略型別;雖然不用寫型別,但 Scala 依然是 <strong>Strong-Type</strong>,變數的型別一旦決定後,就不能更改,這一點和 Java 是一樣的。</p>
<p><strong>In Java</strong></p>
<pre><code>public class Test { }
Test a = new Test();
</code></pre>
<p><strong>In Scala</strong></p>
<pre><code>/* Scala */
class Test
val a = new Test()
</code></pre>
<h3 id="toc_4">Import Package 宣告</h3>
<p>Scala 在 import package 有更彈性的宣告方式:</p>
<p><strong>萬用字元</strong></p>
<pre><code>/* 等同 Java: import java.io.* */
import java.io._
/* 等同 Java Static import */
import org.apache.commons.lang3.StringUtils._
</code></pre>
<p><strong>相同 Package 下的 Class 整理成一行</strong></p>
<pre><code>/* 等同 Java
* import java.nio.file.Files
* import java.nio.file.Paths
*/
import java.nio.file.{ Files, Paths }
</code></pre>
<p><strong>常用或太長的 Class Name 取別名</strong></p>
<pre><code>/* Alias Name */
import org.apache.commons.lang3.{ StringUtils => su }
su.split(str, ",")
</code></pre>
<h3 id="toc_5">Ruturn Value of Assignment</h3>
<p>在 Scala 指定值後,會回傳 <strong>Unit</strong>,並不會像 Java 回傳指定的值。</p>
<p><strong>In Java</strong></p>
<pre><code>int a = 10;
int b = a = 30;
</code></pre>
<p><strong>b 的值是 30。</strong></p>
<p><strong>In Scala</strong></p>
<pre><code>var a = 10
val b = a = 30
</code></pre>
<p><strong>b 的值是 Unit。</strong></p>
<h3 id="toc_6">OO Access Level</h3>
<ul>
<li>Java
<ul>
<li>在 Java 沒有指定時,是 <strong>package-private</strong>。</li>
<li>可存取同 <strong>package</strong> 的 <strong>protected</strong> 變數。</li>
</ul></li>
</ul>
<p><a href="http://docs.oracle.com/javase/tutorial/java/javaOO/accesscontrol.html">Java 說明文件</a></p>
<p><img src="http://docs.oracle.com/javase/tutorial/figures/java/classes-access.gif" alt="實例" title="實例"></p>
<table>
<thead>
<tr>
<th style="text-align: center">Modifier</th>
<th style="text-align: center">Class</th>
<th style="text-align: center">Package</th>
<th style="text-align: center">Subclass</th>
<th style="text-align: center">World</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: center">public</td>
<td style="text-align: center">Y</td>
<td style="text-align: center">Y</td>
<td style="text-align: center">Y</td>
<td style="text-align: center">Y</td>
</tr>
<tr>
<td style="text-align: center">protected</td>
<td style="text-align: center">Y</td>
<td style="text-align: center">Y</td>
<td style="text-align: center">Y</td>
<td style="text-align: center">N</td>
</tr>
<tr>
<td style="text-align: center">no modifier <br /> (<strong>package-private</strong>)</td>
<td style="text-align: center">Y</td>
<td style="text-align: center">Y</td>
<td style="text-align: center">N</td>
<td style="text-align: center">N</td>
</tr>
<tr>
<td style="text-align: center">private</td>
<td style="text-align: center">Y</td>
<td style="text-align: center">N</td>
<td style="text-align: center">N</td>
<td style="text-align: center">N</td>
</tr>
</tbody>
</table>
<table>
<thead>
<tr>
<th style="text-align: center">Modifier</th>
<th style="text-align: center">Alpha</th>
<th style="text-align: center">Beta</th>
<th style="text-align: center">AlphaSub</th>
<th style="text-align: center">Gamma</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: center">public</td>
<td style="text-align: center">Y</td>
<td style="text-align: center">Y</td>
<td style="text-align: center">Y</td>
<td style="text-align: center">Y</td>
</tr>
<tr>
<td style="text-align: center">protected</td>
<td style="text-align: center">Y</td>
<td style="text-align: center">Y</td>
<td style="text-align: center">Y</td>
<td style="text-align: center">N</td>
</tr>
<tr>
<td style="text-align: center">no modifier <br /> (<strong>package-private</strong>)</td>
<td style="text-align: center">Y</td>
<td style="text-align: center">Y</td>
<td style="text-align: center">N</td>
<td style="text-align: center">N</td>
</tr>
<tr>
<td style="text-align: center">private</td>
<td style="text-align: center">Y</td>
<td style="text-align: center">N</td>
<td style="text-align: center">N</td>
<td style="text-align: center">N</td>
</tr>
</tbody>
</table>
<ul>
<li>Scala
<ul>
<li>Scala 拿掉 <strong>public</strong> modifier</li>
<li>在 Scala 沒有指定時,則是 <strong>public</strong>。</li>
<li><strong>不能</strong>存取同 <strong>package</strong> 的 <strong>protected</strong> 變數。</li>
</ul></li>
</ul>
<table>
<thead>
<tr>
<th style="text-align: center">Modifier</th>
<th style="text-align: center">Alpha</th>
<th style="text-align: center">Beta</th>
<th style="text-align: center">AlphaSub</th>
<th style="text-align: center">Gamma</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: center">no modifier <br /> (<strong>public</strong>)</td>
<td style="text-align: center">Y</td>
<td style="text-align: center">Y</td>
<td style="text-align: center">Y</td>
<td style="text-align: center">Y</td>
</tr>
<tr>
<td style="text-align: center">protected</td>
<td style="text-align: center">Y</td>
<td style="text-align: center">N</td>
<td style="text-align: center">Y</td>
<td style="text-align: center">N</td>
</tr>
<tr>
<td style="text-align: center">private</td>
<td style="text-align: center">Y</td>
<td style="text-align: center">N</td>
<td style="text-align: center">N</td>
<td style="text-align: center">N</td>
</tr>
</tbody>
</table>
<p>總結</p>
<table>
<thead>
<tr>
<th style="text-align: center">Modifier</th>
<th style="text-align: center">Scala</th>
<th style="text-align: center">Java</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: center">no modifier</td>
<td style="text-align: center">public</td>
<td style="text-align: center">package-private</td>
</tr>
<tr>
<td style="text-align: center">protected</td>
<td style="text-align: center">自己, subclass</td>
<td style="text-align: center">自己, subclass, 同 package</td>
</tr>
</tbody>
</table>
<h3 id="toc_7">沒有 break 及 continue 關鍵字</h3>
<p>Scala 拿掉了 break 及 continue 功能,雖然有替代方案,不建議使用。</p>
<h3 id="toc_8">保有 return 功能,但不建議使用</h3>
<p>Scala 為了遵守 Functional Lanaguage 精神,不建議在程式隨時可以 return 來中斷執行。雖然 <code>return</code> 還可以使用,但不建議使用。</p>
<p>如果需要回傳值,請在函式的最後一行,指定回傳值。 Ex:</p>
<pre><code>def test(): Int = {
val a = 10
var b = a + 20
b /* 回傳 b 的值 */
}</code></pre>
<h3 id="toc_9">單一檔案,不限制只能有一個 public class</h3>
<p>Java 的程式碼檔案(.java),只能有一個 public class,且檔名須與 public class 同名;Scala 則無此限制。</p>
<pre><code>/* Test.scala */
class Test1
class Test2
</code></pre>
<h3 id="toc_10">Java Primitive type 對應</h3>
<p>Java 有 primitive type (ex: int, long 等),但 Scala 都轉成物件 (AnyVal)來處理。ex: scala.Int, scala.Long。但在 compiler 時,依視程式的邏輯,再看是否要轉回 java primitive type。</p>
<h3 id="toc_11">不要再使用 null,改用 Option</h3>
<p>在 Scala ,雖然 <code>null</code> 依然存在,但強烈建議不要再使用。如果原在 Java 的邏輯中,需要回傳 null 者,請都改用 <code>Option</code> 回傳。 </p>
<h3 id="toc_12">Checked Exception 不再強制要 try - catch</h3>
<p>Java 在 checked exception (ex: IOException, SQLException) 都會強制要用 try - catch。在 Scala 則無。所以在寫程式時,要注意使用的函式,是否會 throw exception。Scala 有提供 Exception handle 的方式,其中一種如下(<code>Try</code>):</p>
<pre><code>/* Java */
public Integer parseInt(String str) {
try {
return Integer.parseInt(str);
}
catch(Exception ex) {
return null;
}
}
/* Scala */
def parseInt(str: String) = Try {
str.toInt
}
</code></pre>
<h3 id="toc_13"><code>equals</code> 可以回到改用 <code>==</code></h3>
<p>原本 Java 的 <code>==</code> 是用在比對 reference 值,要比對內容值要用 <code>equals</code>。在 Scala 可以直接用 <code>==</code>,要比對 reference 值,改用 <code>eq</code>。</p>
<pre><code>/* Java */
String str = "hello world!";
"hello".equals(str);
/* Scala */
val str = "Hello world!"
"hello" == str
</code></pre>
<h3 id="toc_14">沒有 <code>++</code>, <code>--</code></h3>
<p>Scala 拿掉 <code>++</code>, <code>--</code>。請改用 <code>+=</code>, <code>-=</code>。</p>
<script type="text/x-mathjax-config">
if (typeof MathJaxListener !== 'undefined') {
MathJax.Hub.Register.StartupHook('End', function () {
MathJaxListener.invokeCallbackForKey_('End');
});
}
</script>
<script type="text/javascript" src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
</div>kigihttp://www.blogger.com/profile/09881273562560954871noreply@blogger.com0tag:blogger.com,1999:blog-6312720754439261813.post-64079354963776007032015-07-25T22:37:00.001+08:002015-07-25T22:37:14.070+08:00Introduction of Akka Supervisor Strategy (Akka Supervisor Strategy 簡介)<div class='mymarkdown'>
<h1 id="toc_0">Introduction of Akka Supervisor Strategy</h1>
<p>每一個 Akka Actor 都會有一個 supervisor。Supervisor 會監控它產生的子 Actor,並且決定在有 Exception 發生時,該如何處理。我們實作 Actor 時,可以不去 override 它,Akka 本身有預設的 supervisor strategy. </p>
<p>Akka 的 supervisor strategy 有兩種:</p>
<ul>
<li>OneForOneStrategy</li>
<li>AllForOneStrategy</li>
</ul>
<p>簡單來說,<code>OneForOneStrategy</code> 是其中某個 actor 發生問題時,不會去影響到它的兄弟姊妹,而 <code>AllForOneStrategy</code> 則會。Akka 自身預設是 <code>OneForOneStrategy</code>。</p>
<p>Akka 提供四種決定,</p>
<ul>
<li>Resume:繼續,<strong>不會 reset</strong> Actor 的資料</li>
<li>Restart:重啟 Actor,<strong>會 reset</strong> Actor 的資料</li>
<li>Stop: 中止 Actor</li>
<li>Escalate: 將問題往上報,讓上層的 supervisor 來決定。</li>
</ul>
<p>Akka Actor 有一個特性,就是 Actor 的資料跟工作是分開的,因此我們可以選擇用 <code>Resume</code> 的方式,保留資料,繼續往下處理。</p>
<p>Akka 預設的 strategy 處理方式</p>
<ul>
<li><code>ActorInitializationException</code> => <code>Stop</code></li>
<li><code>ActorKilledException</code> => <code>Stop</code></li>
<li>其他的 Exception => <code>Restart</code></li>
<li>其他的 Throwable => <code>Escalate</code></li>
</ul>
<p>所以如果我們都不作任何修改的話,當有 <code>Exception</code> 發生時,Akka 會自動重啟 Actor。這樣子的方式,已經符合大部分的需求。</p>
<p>參考資料:</p>
<ul>
<li><a href="http://doc.akka.io/docs/akka/2.3.12/general/supervision.html">Akka Supervision and Monitoring</a></li>
<li><a href="http://doc.akka.io/docs/akka/2.3.12/scala/fault-tolerance.html">Fault Tolerance</a></li>
</ul>
<h2 id="toc_1">實作</h2>
<p><strong>SupervisorDemo</strong> in <a href="https://github.com/kigichang/akka-sample">Sample Code</a></p>
<p>延用 <strong>吃飯睡覺打東東</strong> 的企鵝笑話,<strong>東東</strong>,在被打第 <strong>4</strong> 次時,會說 <strong>不要打擾我</strong>,在第 <strong>6</strong> 次時,會 <strong>爆炸</strong>,在超過第 <strong>6</strong> 次後,牠就<strong>成仙</strong>了。本程式,主要是由 <code>PenguinKing</code> 來產生 <code>Penguin</code>,也因此 <code>PenguinKing</code> 是 <code>Penguin</code> 的 supervisor。各位可以在 <code>PenguinKing</code> 的程式碼中,修改</p>
<pre><code>override val supervisorStrategy =
OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 1 minute) {
//AllForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 1 minute) {
case _ : DontBotherMeException => Resume
case _ : ExplodeException => Restart
case _ : IamGodException => Stop
case _ : Exception => Escalate
}</code></pre>
<p>或註解掉這一段程式,來了解 Akka Stragegy 的運作方式。</p>
<p>尤其注意在 <code>Resume</code> 後,<code>Penguin</code> 的 <code>hits</code> 值,並沒有被 reset。</p>
<p>你可以依照以下的方式修改程式,觀察輸出的結果,來了解 Akka 的 Supervisor Strategy 運作方式</p>
<ul>
<li><p>在本 sample code 中,如果都不修改的話,是不會進到 <code>IamGodException</code>,因為在 <code>ExplodeException</code>後,會重啟 Actor,而 <code>hits</code> 值就會被改成 <code>0</code>,重頭計算。</p></li>
<li><p>修改 <code>ExplodeException => Resume</code>,則會進到 <code>IamGodException</code>, 此時 <strong>東東</strong> 就會停止了,不能再使用。</p></li>
<li><p>修改 <code>ExplodeException => Escalate</code>,則會發現全部的 <code>Penguin</code> actor 都被重啟了,也包括 <code>PenguinKing</code>。</p></li>
<li><p>將 <code>OneForOneStrategy</code> 改成 <code>AllForOneStrategy</code>,則如果 <strong>東東</strong> 被重啟了,其他的 <strong>企鵝</strong> 也會被重啟。</p></li>
</ul>
<script type="text/x-mathjax-config">
if (typeof MathJaxListener !== 'undefined') {
MathJax.Hub.Register.StartupHook('End', function () {
MathJaxListener.invokeCallbackForKey_('End');
});
}
</script>
<script type="text/javascript" src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
</div>kigihttp://www.blogger.com/profile/09881273562560954871noreply@blogger.com0tag:blogger.com,1999:blog-6312720754439261813.post-27121973146750428062015-07-25T22:33:00.000+08:002015-07-25T22:34:34.870+08:00Introduction of Akka Routing (Akka Routing 簡介)<div class='mymarkdown'>
<h1 id="toc_0">Introduction of Akka Routing</h1>
<p>當產生多個 actor 來處理相同事件時, Akka 提供 <code>Router</code> 的機制,來有效分配 message 給 actor 來完成工作。 在 Akka 中,受 <code>Router</code> 管理的 actor 稱作 <code>Routee</code>。</p>
<p>Akka 提供以下 routing 的管理方式:</p>
<ul>
<li><code>akka.routing.RoundRobinRoutingLogic</code></li>
<li><code>akka.routing.RandomRoutingLogic</code></li>
<li><code>akka.routing.SmallestMailboxRoutingLogic</code></li>
<li><code>akka.routing.BroadcastRoutingLogic</code></li>
<li><code>akka.routing.ScatterGatherFirstCompletedRoutingLogic</code></li>
<li><code>akka.routing.ConsistentHashingRoutingLogic</code></li>
</ul>
<p>參考資料:</p>
<p><a href="http://doc.akka.io/docs/akka/2.3.12/scala/routing.html">Akka Routing</a></p>
<p>實作:</p>
<p><strong>RouterDemo</strong> in <a href="https://github.com/kigichang/akka-sample">Sample Code</a></p>
<p>延用 <strong>吃飯、睡覺、打東東</strong> 的範列,由 <code>PenguinKing</code> 產生 <code>Penguin</code>。<code>PenguinManager</code> 擔任 Router 角色,分配工作。<code>Reporter</code> 會透過 <code>PenguinManager</code> 詢問所有的企鵝,共 100 次,每隻企鵝會記錄被問了幾次,並回報給記者。</p>
<p>如何使用 <code>Router</code></p>
<pre><code>var router = {
val routees = Vector.fill(5) {
val r = context.actorOf(Props[Worker])
context watch r
ActorRefRoutee(r)
}
Router(RoundRobinRoutingLogic(), routees)
}
</code></pre>
<p>使用 Router 很簡單,首先將產生的 <code>ActorRef</code> 利用 <code>ActorRefRoutee</code>,再加入 <code>Router</code> 並設定此 <code>Router</code> 要使用那種管理方式,如上例使用 <code>RoundRobinRoutingLogic</code>。</p>
<p>使用 Router 時,記得把變數設成 <code>var</code>,因為當要加入/移除 Actor 時,Akka 會重新產生新的 <code>Router</code>,此時必須 re-assign 變數。</p>
<p><code>PenguinManager</code>:</p>
<pre><code>/**
* 企鵝總管
*/
class PenguinManager extends Actor {
var router = Router(RoundRobinRoutingLogic())
def receive: Actor.Receive = {
case PenguinReady(actor) =>
context watch actor
router = router.addRoutee(actor)
println("manager watch " + actor.path)
case Terminated(actor) =>
println("manager remove " + actor.path)
router = router.removeRoutee(actor)
case Interest =>
router.route(Interest, sender)
case QueryCount =>
router.routees foreach { actor =>
actor.send(QueryCount, sender)
}
case Hit =>
router.route(Hit, sender)
case KillOne =>
router.routees(0).send(PoisonPill, self)
case _ =>
}
}
object PenguinManager {
val props = Props[PenguinManager]
}
</code></pre>
<p>如果要進一步監控 actor,可以使用 <code>watch</code> ,如上例 <code>context watch actor</code>,當被監控 actor 中止時,監控者會收到 <code>Terminated</code> 的訊息,其中會指名那個 actor 已經中止。如下:</p>
<pre><code>case Terminated(actor) =>
println("manager remove " + actor.path)
router = router.removeRoutee(actor)
</code></pre>
<p>如果要測試不同的 routing 的方式,則修改 <code>var router = Router(RoundRobinRoutingLogic())</code> 即可。</p>
<script type="text/x-mathjax-config">
if (typeof MathJaxListener !== 'undefined') {
MathJax.Hub.Register.StartupHook('End', function () {
MathJaxListener.invokeCallbackForKey_('End');
});
}
</script>
<script type="text/javascript" src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
</div>kigihttp://www.blogger.com/profile/09881273562560954871noreply@blogger.com0tag:blogger.com,1999:blog-6312720754439261813.post-49142557526607156862015-07-25T22:28:00.001+08:002015-07-25T22:28:18.917+08:00Introduction of Akka Remote (Akka Remote 簡介)<div class='mymarkdown'>
<h1 id="toc_0">Introduction of Akka Remote</h1>
<p>Akka 允許遠端執行 Actor, 而且內建小型的 message server,在開發時,完全不用去理會底層網路的建置。使用 Akka Remote 功能,要特別注意每台機器要彼此看得到;也就是說在網段的安排上,每台機器要彼此可以互連。</p>
<p>Akka 遠端執行,分成兩種模式如下:</p>
<h2 id="toc_1">模式</h2>
<h3 id="toc_2">Lookup</h3>
<p>Lookup 是在遠端 Server,先啟動 Actor,再讓 Client 連線使用。這種模式很類似傳統的 Client/Server 架構。</p>
<h3 id="toc_3">Deployment</h3>
<p>由 Master,指定遠端的 Node 要執行那些 Actor。在佈署環境時,還是需要將 Actor 放到 Node。Node 啟動時,並不會去預載要執行的 Actor,而是等待 Master 端來指定。</p>
<h2 id="toc_4">Actor 基本運作</h2>
<p>在使用 Remote 功能前,先要了解 Actor 基本運作。</p>
<h3 id="toc_5">Actor 階層關係</h3>
<p>每一個 Actor 都有一個 Supervisor,包含系統內建及自建的 Actor。如下圖:</p>
<p><img src="http://doc.akka.io/docs/akka/2.3.11/_images/guardians.png" alt="The Top-Level Supervisors"></p>
<p><strong>Image from: <a href="http://doc.akka.io/docs/akka/2.3.11/general/supervision.html">Supervision and Monitoring</a></strong></p>
<p>我們自己產生的 Actor 會被歸類在 <code>user</code> 這一群。請特別注意 <strong>user</strong>。<strong>user</strong> 在後面的談到的 Actor Path 會利用到。</p>
<p>在容錯的機制中,Supervisor 需要扮演監控及重啟 Child Actor 的角色,Akka 本身已有內建監控與重啟的 <code>Strategy</code>。如果沒有特別去 orverride,就會套用預設的機制。</p>
<h3 id="toc_6">Actor Path</h3>
<p>每一個 Actor 都會有一個 Actor Path,如下:</p>
<pre><code>"akka://my-sys/user/service-a/worker1" // purely local
"akka.tcp://my-sys@host.example.com:5678/user/service-b" // remote</code></pre>
<p><img src="http://doc.akka.io/docs/akka/2.3.11/_images/RemoteDeployment.png" alt="The Interplay with Remote Deployment"></p>
<p><strong>Code and image from <a href="http://doc.akka.io/docs/akka/2.3.11/general/addressing.html">Actor References, Paths and Addresses</a></strong></p>
<p>簡單來說,在單機時:</p>
<pre><code>val system = ActorSystem("HelloWorld")
val actor = system.actorOf(Props[MyActor], "MyHelloWorld")</code></pre>
<p>上面 <code>actor</code> 的 path 會是 <code>akka://HelloWorld/user/MyHelloWorld</code> 也就是 <code>akka://System_Name/user/Actor_Name</code>,其中要特別注意 <strong>user</strong>,所以自己產生的 actor 都會被歸到 <strong>user</strong> 路徑下。</p>
<p>同理在遠端上 Actor path 就會像 <code>akka.tcp://HelloWorld@host:port/user/MyHelloWorld</code>。</p>
<p>如果某個 actor 是被另一個 actor 產生時,則 path 會像:</p>
<pre><code>akka://HelloWorld/user/parent/MyHelloWorld</code></pre>
<p>Actor path 在系統中,是不能重覆的,因此在為 actor 命名時,要小心。</p>
<h2 id="toc_7">實作 - 把企鵝丟到遠處</h2>
<p>延用上一篇 <strong>吃飯、睡覺、打東東</strong> 的梗,將企鵝的 Actor 放在遠端的機器上執行。<a href="https://github.com/kigichang/akka-sample">Source code</a> 放在我的 GitHub 上。</p>
<h3 id="toc_8">前置工作</h3>
<ul>
<li><p>首先要修改 <code>libraryDependencies</code> 原本是用 <del><code>"com.typesafe.akka" %% "akka-actor" % "2.3.4"</code></del> <code>"com.typesafe.akka" %% "akka-actor" % "2.3.11"</code>, 改成 Remote 版本 <del><code>"com.typesafe.akka" %% "akka-remote" % "2.3.4"</code></del> <code>"com.typesafe.akka" %% "akka-remote" % "2.3.11"</code></p></li>
<li><p>在專案的目錄,新加 <code>src/main/resources</code><del>,並且加到 eclipse 專案的 <code>Source PATH</code>。這個目錄等一下要放設定檔。</del></p></li>
</ul>
<h3 id="toc_9">問題</h3>
<pre><code>package com.example
sealed trait Question
object Interest extends Serializable with Question
object Why extends Serializable with Question
</code></pre>
<p>Singleton 要繼承 <strong>Serializable</strong> 否則訊息會傳不出去。</p>
<h3 id="toc_10">回答</h3>
<p>新增 <code>PenguinReady</code></p>
<pre><code>package com.example
sealed trait Answer
case class Three(name: String, a: String, b: String, c: String) extends Answer
case class Two(name: String, a: String, b: String) extends Answer
case class Because(name: String, msg: String) extends Answer
case class PenguinReady(actor: ActorRef)
</code></pre>
<h3 id="toc_11">角色</h3>
<p>原本的角色,再新增 <code>LookupReporter</code>,<code>PenguinKing</code>,<code>DepolyReporter</code>。</p>
<pre><code>package com.example
import akka.actor.Actor
import akka.actor.Props
import akka.actor.Identify
import scala.concurrent.duration.DurationInt
import akka.actor.ActorIdentity
import scala.concurrent.duration.Duration
import akka.actor.ActorRef
/**
* 企鵝
*/
class Penguin(val name: String) extends Actor {
def receive: Actor.Receive = {
case Interest =>
println(s"$name got Interest")
sender() ! Three(name, "吃飯", "睡覺", "打東東")
case Why =>
case _ =>
}
override def preStart() = {
println(s"$name start at ${self.path}")
}
}
object Penguin {
def apply(name: String): Props = Props(classOf[Penguin], name)
}
/**
* 叫東東的企鵝
*/
class DongDong extends Penguin("東東") {
override def receive = {
case Interest =>
println(s"$name got Interest")
sender() ! Two(name, "吃飯", "睡覺")
case Why =>
println(s"$name got Why")
sender() ! Because(name, s"我就是【${name}】")
case _ =>
}
}
object DongDong {
val props = Props[DongDong]
}
/**
* 記者
*/
class Reporter extends Actor {
def receive: Actor.Receive = {
/* 有三個興趣的回覆 */
case Three(name, a, b, c) =>
println(s"${name}: ${a}, ${b}, ${c}")
/* 只有二個興趣的回覆,反問 why */
case Two(name, a, b) =>
println(s"${name}: ${a}, ${b}")
sender ! Why
/* 接到 why 的回覆 */
case Because(name, msg) =>
println(s"${name}: ${msg}")
case _ =>
}
}
object Reporter {
val props = Props[Reporter]
}
/**
* Lookup 版的記者
*/
class LookupReporter(penguins: Array[String]) extends Reporter {
var count = 0
def sendIdentifyRequest() {
penguins foreach { path => println(path); context.actorSelection(path) ! Identify(path) }
context.setReceiveTimeout(5 seconds)
}
sendIdentifyRequest
def myReceive: Actor.Receive = {
case ActorIdentity(path, Some(actor)) =>
count += 1
if (count == penguins.length) {
context.setReceiveTimeout(Duration.Undefined)
}
println(s"${path} found")
actor ! Interest
case ActorIdentity(path, None) =>
println(s"${path} not found")
}
override def receive: Actor.Receive = myReceive orElse super.receive
override def preStart() = {
println("Lookup Reporter start")
}
}
object LookupReporter {
def apply(penguins: Array[String]) = Props(classOf[LookupReporter], penguins)
}
/**
* 企鵝王
*/
class PenguinKing(count: Int, reporter: ActorRef) extends Actor {
def depoly = {
for (i <- 0 until count - 1) {
val penguin = context.actorOf(Penguin(s"penguin-$i"))
penguin ! Identify(penguin.path.toString)
}
val dongdong = context.actorOf(DongDong.props)
dongdong ! Identify(dongdong.path.toString)
}
depoly
def receive: Actor.Receive = {
case ActorIdentity(path, Some(actor)) =>
println(s"$path found")
reporter ! PenguinReady(actor)
case ActorIdentity(path, None) =>
println(s"$path not found")
}
}
object PenguinKing {
def apply(count: Int, reporter: ActorRef) = Props(classOf[PenguinKing], count, reporter)
}
/**
* Depolyment 版記者
*/
class DepolyReporter extends Reporter {
def myReceive: Actor.Receive = {
case PenguinReady(actor) =>
actor ! Interest
}
override def receive = myReceive orElse super.receive
}
object DepolyReporter {
val props = Props[DepolyReporter]
}
</code></pre>
<h3 id="toc_12">Lookup 模式</h3>
<p>Lookup 模式像 Client/Server,比較好理解。程式分成 <code>LookupServer</code> 及 <code>LookupClient</code>。執行順序:<code>LookupServer</code> -> <code>LookupClient</code>. </p>
<ul>
<li>Lookup Server</li>
</ul>
<pre><code>package com.example
import com.typesafe.config.ConfigFactory
import akka.actor.ActorRef
import akka.actor.ActorSystem
object LookupServer {
def main(args: Array[String]) {
val system = ActorSystem("LookupServer", ConfigFactory.load("lookup-server"))
val penguins = new Array[ActorRef](10)
for (i <- 0 to 8) {
penguins(i) = system.actorOf(Penguin(s"Penguin-$i"), s"penguin-$i")
println(penguins(i).path)
}
penguins(9) = system.actorOf(DongDong.props, "dongdong")
println(penguins(9).path)
}
}
</code></pre>
<p>程式跟先前的差不多,主要差別在產生 ActorSystem 時,多了一個 <code>ConfigFactory.load("lookup-server")</code>,這個就是先前提到的設定檔,在開發環境下,會去 src/main/resources/ 目錄下找 <code>lookup-server.conf</code></p>
<ul>
<li>lookup-server.conf</li>
</ul>
<pre><code>akka {
actor {
provider = "akka.remote.RemoteActorRefProvider"
}
remote {
netty.tcp {
hostname = "127.0.0.1"
port = 2552
}
}
}
</code></pre>
<p>原則上,只要寫好設定檔,在程式開好 Actor,就完成一個可以被遠端呼叫的 Server。</p>
<ul>
<li>Lookup Client</li>
</ul>
<pre><code>package com.example
import akka.actor.ActorSystem
import com.typesafe.config.ConfigFactory
object LookupClient {
def main(args: Array[String]) {
val system = ActorSystem("LookupClient", ConfigFactory.load("lookup-client"))
val remotePath = "akka.tcp://LookupServer@127.0.0.1:2552/user/"
val penguins = new Array[String](10)
for (i <- 0 to 8) {
penguins(i) = s"${remotePath}penguin-${i}"
}
penguins(9) = s"${remotePath}dongdong"
val reporter = system.actorOf(LookupReporter(penguins), "reporter")
println(reporter.path)
/* 主程式等一下,要不然上面都是 non-blocking call,會直接結束程式 */
Thread.sleep(5 * 1000)
system.shutdown
println("end")
}
}
</code></pre>
<ul>
<li>lookup-client.conf</li>
</ul>
<pre><code>akka {
actor {
provider = "akka.remote.RemoteActorRefProvider"
}
remote {
netty.tcp {
hostname = "127.0.0.1"
port = 2554
}
}
}
</code></pre>
<p>Client 程式,先準備好遠端 Actor Path,然後傳給 <code>LookupReporter</code> 去呼叫遠端的 <code>Penguin</code>。</p>
<ul>
<li>LookupReporter</li>
</ul>
<pre><code>class LookupReporter(penguins: Array[String]) extends Reporter {
var count = 0
def sendIdentifyRequest() {
penguins foreach { path => println(path); context.actorSelection(path) ! Identify(path) }
context.setReceiveTimeout(5 seconds)
}
sendIdentifyRequest
def myReceive: Actor.Receive = {
case ActorIdentity(path, Some(actor)) =>
count += 1
if (count == penguins.length) {
context.setReceiveTimeout(Duration.Undefined)
}
println(s"${path} found")
actor ! Interest
case ActorIdentity(path, None) =>
println(s"${path} not found")
}
override def receive: Actor.Receive = myReceive orElse super.receive
override def preStart() = {
println("Lookup Reporter start")
}
}
object LookupReporter {
def apply(penguins: Array[String]) = Props(classOf[LookupReporter], penguins)
}
</code></pre>
<p>在 <code>LookupReporter</code> 中,我們使用 Actor 內建的 <code>context</code>,來查詢遠端的 actor: <code>context.actorSelection(path)</code>。</p>
<p><code>LookupReporter</code> 跟先前一樣,但多了一個 <code>sendIdentifyRequest</code> 的函式,主要的功能是查詢遠端的 actor,是否已經 ready。在 Akka Actor 內建處理 <code>Identify</code> 的功能,我們可以對某個 Actor 傳送 <code>Identify</code>, <code>Identify</code> 的參數可以自定,用來辨識。當 Actor 是 OK 的話,則會收到 <code>ActorIdentity(id, Some(actor))</code>,其中的 <code>id</code> 就是先前在 <code>Identify</code> 加入的辨識字串。如果 Actor 沒有 Ready 好的話,則會收到 <code>ActorIdentity(id, None)</code>, 這時候,我們就可以知道那個 Actor 掛了。</p>
<p>執行起來的結果,你可以在 Server 的 console 上看到 <code>Penguin</code> output 的訊息,在 Client 端這邊,看到 <code>LookupReporter</code> 的 output。</p>
<h3 id="toc_13">Deployment 模式</h3>
<p>Deployment 模式,Master 可以指定 Actor 在那個遠端的 Node 來執行。這個範例純用設定檔的方式,來佈署 Actor。Akka 也允許在程式內,自行動態佈署。</p>
<p>執行順序:<code>DepolyNode</code> -> <code>DepolyMaster</code></p>
<ul>
<li>Deploy Node - 用來執行被佈署的 Actor</li>
</ul>
<pre><code>package com.example
import akka.actor.ActorSystem
import com.typesafe.config.ConfigFactory
object DepolynNode {
def main(args: Array[String]) {
val system = ActorSystem("DepolyNode", ConfigFactory.load("depoly-node"))
}
}
</code></pre>
<p>由上的範例,其實我們只要啟一個 <code>ActorSystem</code> 即可。</p>
<ul>
<li>deploy-node.conf</li>
</ul>
<pre><code>akka {
actor {
provider = "akka.remote.RemoteActorRefProvider"
}
remote {
netty.tcp {
hostname = "127.0.0.1"
port = 2551
}
}
}
</code></pre>
<p>其實跟上面的 <code>lookup-server.conf</code> 相似。</p>
<ul>
<li>deploy-master.conf</li>
</ul>
<pre><code>akka {
actor {
provider = "akka.remote.RemoteActorRefProvider"
deployment {
"/penguin-king/*" {
remote = "akka.tcp://DepolyNode@127.0.0.1:2551"
}
}
}
remote {
netty.tcp {
hostname = "127.0.0.1"
port = 2553
}
}
}
</code></pre>
<p>在 client 程式前,我們先看一下 client 的 conf 檔。跟先前的 <code>lookup-client.conf</code> 差不多,但多了一個 <code>deployment</code> 設定。上面的設定,是說明要將 actor path 是 <code>/penguin-king/*</code> 送到遠端 Node 執行。</p>
<ul>
<li>Deploy Master - 指定 Actor 在那些 Node 執行</li>
</ul>
<pre><code>package com.example
import com.typesafe.config.ConfigFactory
import akka.actor.ActorSystem
object DepolyMaster {
def main(args: Array[String]) {
val system = ActorSystem("DepolyMaster", ConfigFactory.load("depoly-master"))
val reporter = system.actorOf(DepolyReporter.props)
val king = system.actorOf(PenguinKing(10, reporter), "penguin-king")
Thread.sleep(10 * 1000)
system.shutdown()
println("end")
}
}
</code></pre>
<p><code>DepolyMaster</code> 開啟一個 <code>PenguinKing</code> 的 actor,且註冊 actor name 是 <code>penguin-king</code>。<code>PenguinKing</code> 用來產生 <code>Penguin</code> 及 <code>DongDong</code>。如下:</p>
<pre><code>def depoly = {
for (i <- 0 until count - 1) {
val penguin = context.actorOf(Penguin(s"penguin-$i"))
penguin ! Identify(penguin.path.toString)
}
val dongdong = context.actorOf(DongDong.props)
dongdong ! Identify(dongdong.path.toString)
}
</code></pre>
<p>在 <code>Actor</code> 中有 <code>context</code> 可以用來產生子 actor。由於 <code>PenguinKing</code> 的 actor name 是 <code>penguin-king</code>,這些子 <code>Penguin</code> 的 path 就會變成 <code>/penguin-king/xx</code>,就符合我們在 <code>deploy-master.conf</code> 設定檔上設定的 deployment 路徑 <code>/penguin-king/*</code>,佈署後,一樣使用 <code>Identify</code> 來確認是否完成佈署。當完成佈署後,通知 <code>DepolyReporter</code> 可以問題。</p>
<p><code>PenguinKing</code> 的 <code>receive</code> 如下:</p>
<pre><code>def receive: Actor.Receive = {
case ActorIdentity(path, Some(actor)) =>
println(s"$path found")
reporter ! PenguinReady(path.toString)
case ActorIdentity(path, None) =>
println(s"$path not found")
}
</code></pre>
<p><code>DepolyReporter</code> 的 <code>receive</code> 如下:</p>
<pre><code>def myReceive: Actor.Receive = {
case PenguinReady(path) =>
val actor = context.actorSelection(path)
actor ! Interest
}
override def receive = myReceive orElse super.receive
</code></pre>
<p>執行結果會像 lookup 模式,在 Server 端看到 <code>Penguin</code> 的 output,在 Client 端看到 <code>DepolyReporter</code> 的 output。</p>
<script type="text/x-mathjax-config">
if (typeof MathJaxListener !== 'undefined') {
MathJax.Hub.Register.StartupHook('End', function () {
MathJaxListener.invokeCallbackForKey_('End');
});
}
</script>
<script type="text/javascript" src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
</div>kigihttp://www.blogger.com/profile/09881273562560954871noreply@blogger.com0tag:blogger.com,1999:blog-6312720754439261813.post-19704538325811845032015-07-25T22:18:00.000+08:002015-07-25T22:18:35.719+08:00Introduction of Akka (Akka 簡介)<div class='mymarkdown'>
<h1 id="toc_0">Introduction of Akka</h1>
<p>Akka 是多工運算的框架,用 Scala 撰寫,也有支援 Java API。Akka 最主要的目的是要解決 Synchronize 造成的效能問題,以及可能發生的 Dead-Lock 問題。</p>
<p>相關資料:</p>
<ul>
<li><a href="http://akka.io/docs/">Akka 官方資料</a></li>
<li><a href="https://typesafe.com/platform/getstarted">Typesafe 的 Activator</a></li>
</ul>
<h2 id="toc_1">特性</h2>
<h3 id="toc_2">Non-Blocking and Asynchronous</h3>
<p>所有 Akka 上的運作都是 Non-Blocking,也就是說任何的動作,都會立即回應。因此已經習慣一個指令,等待一個動作的思維,必需要修正,否則寫出來的程式效能不好,也可能有很多 bug。</p>
<p>當然在實際的環境下,我們還是會有 Blocking call 的需求,Scala 本身就有提供這方面的 API 可用。但 Scala 相關的 eco-system 都建議能不用就不用,以免造成效能上的問題。</p>
<h3 id="toc_3">Event-Driven, Fire-And-Forgot and Actor</h3>
<p>Actor 是 Akka 最基本,也是最重要的元素。 Akka 使用 Actor 來完成工作,並且可以透過自定的 message 來互相溝通,或者觸發指定的工作。最簡單的實作概念:你可以使用 Actor 來管理某個系統資源,但有其他的 Actor 需要使用時,可以透過自定的 message 來操作。舉例來說: <code>A</code> 是個管理 Log Queue 的 Actor,負責將傳來的資料,寫入 log 檔案。 當某個 process 有需要寫 log 時,就可以叫出 <code>A</code>,並對 <code>A</code> 傳送包含要寫入資料的 message。當傳送這個 message 後,process 並不會等待回覆的訊息,而是往下繼續自己的工作。</p>
<p>實作 Actor, 其實也是在實作一個 message 的處理。如果有寫過 Windows 相關的程式,就可以很容易想像。Actor 必需實作一個 <code>receive</code> 的 function 來處理當收到某個 message 時,應該要作什麼事情。雷同在 Windows 下某個 Button 被 Click 時,其實是 Windows 系統發出一個 <code>WM_CLICK</code> 給 Button,而我們會去實作 <code>OnClick</code> funtion 來處理收到 <code>WM_CLICK</code>。</p>
<h3 id="toc_4">Fault-tolerance and Let It Crash</h3>
<p>在多工運算下,容錯是件很重要的事情,Akka 採用 <strong>Let It Crash</strong> 的思維,會自動重啟已經 crash 的 Actor,當然重啟的 policy 也可以自定,沒有的話,Akka 在發現有 Exception 發生時,會自動重啟 Actor。Actor 本身的狀態或資料,跟 Actor 的工作是被分開的,也就是說,某個 Actor 在執行工作時,不幸 Crash 時,在還沒有被 <code>Restart</code>,已經修改的資料,是被保留下來的,此時,你可以選擇 <code>Restart</code> 將資料重新 reset 或者用 <code>Resume</code> 來繼續往下的工作。</p>
<h3 id="toc_5">Location Transparency</h3>
<p>Akka 的 Actor 除了可以在本機上執行,也可以指定在遠端的某個機器執行,中間的網路溝通,akka 會自動幫忙處理,但也很重要的一點,你傳送的 message 必須是可以被 serialize。 Akka 是使用 <a href="http://netty.io/">Netty</a> 來處理 network。</p>
<h3 id="toc_6">Message Ordering</h3>
<p>Akka 可以保証來自同一個 Actor 的訊息順序,但不保証每一次的訊息一定會種到。</p>
<p>以下是 Akka 官方有關訊息順序的說明:</p>
<p>The guarantee is illustrated in the following:</p>
<p>Actor <code>A1</code> sends messages <code>M1</code>, <code>M2</code>, <code>M3</code> to <code>A2</code></p>
<p>Actor <code>A3</code> sends messages <code>M4</code>, <code>M5</code>, <code>M6</code> to <code>A2</code></p>
<p>This means that:</p>
<ul>
<li>If <code>M1</code> is delivered it must be delivered before <code>M2</code> and <code>M3</code></li>
<li>If <code>M2</code> is delivered it must be delivered before <code>M3</code></li>
<li>If <code>M4</code> is delivered it must be delivered before <code>M5</code> and <code>M6</code></li>
<li>If <code>M5</code> is delivered it must be delivered before <code>M6</code></li>
</ul>
<p><code>A2</code> can see messages from <code>A1</code> interleaved with messages from <code>A3</code></p>
<p>Since there is no guaranteed delivery, any of the messages may be dropped, i.e. not arrive at <code>A2</code></p>
<h2 id="toc_7">實作</h2>
<p>在實作前,建議去下載 Typesafe 的 Activator 工具,它會自動產生基本 Scala 開發環境設定,省去寫 SBT 的工作。安裝 Activator 很簡單,只要<a href="https://typesafe.com/platform/getstarted">下載</a>,解開就行了。</p>
<p>用 Activator 產生一個空白的專案:</p>
<ul>
<li>執行 <code>activator new project_name minimal-akka-scala-seed</code></li>
<li><del>在 <code>build.sbt</code> 加入 akka library. <code>libraryDependencies += "com.typesafe.akka" %% "akka-actor" % "2.3.4"</code></del></li>
<li>在 <code>project/plugins.sbt</code> 加入 eclipse plugin-in. <code>addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "4.0.0")</code>. <del><code>addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "2.3.0")</code></del></li>
<li>在 project 目錄下,執行 <code>activator eclipse</code> 來產生 Eclipse 的專案檔。</li>
</ul>
<h3 id="toc_8">Akka Hello World</h3>
<p>按慣例,先來個 hello world。以下的範例會產生一個 Actor ,來接收要對誰說 <code>hello, world</code>, 並回覆給原來的傳送者(sender)。</p>
<pre><code>package com.example
import scala.concurrent.duration.DurationInt
import akka.actor.Actor
import akka.actor.ActorSystem
import akka.actor.Inbox
import akka.actor.Props
import akka.actor.actorRef2Scala
case class ToWhom(name: String)
case class Response(msg: String)
class MyActor extends Actor {
def receive: Actor.Receive = {
case ToWhom(name) => sender() ! Response(s"Hi $name. Hello World!!!")
case _ =>
}
}
object MyActor {
val props = Props[MyActor]
}
object HelloWorld {
def main(args: Array[String]) {
/* 啟動 Akka micro-system */
val system = ActorSystem("HelloWorld")
/* 產生一個 Actor。 */
val actor = system.actorOf(MyActor.props)
/* 看一下 Actor 註冊的 path。如果要重覆使用已產生的 Actor,就需要知道 Actor 的 path。
* 這個在遠端執行很重要
* */
val path = actor.path
println(path)
/* demo 使用已產生的 actor */
val actor2 = system.actorSelection(path)
/* 產生一個收信箱. 等一下用此收件箱的名義,對 MyActor 送訊息 */
val inbox = Inbox.create(system)
// 以下是多種傳 message 方式,且都是 non-blocking (Fire-and-Forgot)。
// 方法1
inbox.send(actor, ToWhom("小明"))
// 方法2
//actor.!(ToWhom("小華"))(inbox.getRef)
// 方法3
//actor.tell(ToWhom("東東"), inbox.getRef)
// Blocking call,等待回覆訊息,最多等 5 sec。不等的話,程式會往下執行,就結束了。
val Response(msg) = inbox.receive(5 seconds)
println(msg)
/* 記得要 shutdown, 要不然程式不會結束 */
system.shutdown
println("end")
}
}
</code></pre>
<h3 id="toc_9">小進階多個 Actor 互傳: 吃飯、睡覺、打東東</h3>
<p>實際的情況,都是多個 Actor 彼此間在傳訊息。以下我們就用 <strong>吃飯、睡覺、打東東</strong> 的笑話來 demo。</p>
<p><a href="https://github.com/kigichang/akka-sample">Source Code</a></p>
<h4 id="toc_10">問題:</h4>
<pre><code>package com.example
sealed trait Question
object Interest extends Question
object Why extends Question
</code></pre>
<h4 id="toc_11">回答:</h4>
<pre><code>package com.example
sealed trait Answer
case class Three(name: String, a: String, b: String, c: String) extends Answer
case class Two(name: String, a: String, b: String) extends Answer
case class Because(name: String, msg: String) extends Answer
</code></pre>
<h4 id="toc_12">角色:</h4>
<pre><code>package com.example
import akka.actor.Actor
import akka.actor.Props
/**
* 企鵝
*/
class Penguin(val name: String) extends Actor {
def receive: Actor.Receive = {
case Interest =>
sender() ! Three(name, "吃飯", "睡覺", "打東東")
case Why =>
case _ =>
}
override def preStart() = {
println(s"$name start")
}
}
object Penguin {
def apply(name: String) = Props(classOf[Penguin], name)
}
/**
* 叫東東的企鵝
*/
class DongDong extends Penguin("東東") {
override def receive = {
case Interest =>
sender() ! Two(name, "吃飯", "睡覺")
case Why =>
sender() ! Because(name, s"我就是【${name}】")
case _ =>
}
}
object DongDong {
val props = Props[DongDong]
}
/**
* 記者
*/
class Reporter extends Actor {
def receive: Actor.Receive = {
/* 有三個興趣的回覆 */
case Three(name, a, b, c) =>
println(s"${name}: ${a}, ${b}, ${c}")
/* 只有二個興趣的回覆,反問 why */
case Two(name, a, b) =>
println(s"${name}: ${a}, ${b}")
sender ! Why
/* 接到 why 的回覆 */
case Because(name, msg) =>
println(s"${name}: ${msg}")
case _ =>
}
}
object Reporter {
val props = Props[Reporter]
}
</code></pre>
<h4 id="toc_13">主程式:</h4>
<pre><code>package com.example
import akka.actor.ActorSystem
import akka.actor.ActorRef
object JokeSample {
def main(args: Array[String]) {
val system = ActorSystem("JokeSample")
val reporter = system.actorOf(Reporter.props, "Reporter")
val penguins = new Array[ActorRef](10)
for (i <- 0 to 8) {
penguins(i) = system.actorOf(Penguin(s"Penguin-${i}"))
}
penguins(9) = system.actorOf(DongDong.props)
/* 記者問每隻企鵝,牠的興趣是 */
penguins foreach { _.tell(Interest, reporter) }
/* 主程式等一下,要不然上面都是 non-blocking call,會直接結束程式 */
Thread.sleep(10 * 1000)
system.shutdown()
println("end")
}
}
</code></pre>
<p>由上面程式的 output,就可以很明顯感覺到 non-blocking 的威力,即使我們程式是循序產生企鵝的物件,但是它們被初始化的順序卻不一定,傳送訊息也是如此。</p>
<script type="text/x-mathjax-config">
if (typeof MathJaxListener !== 'undefined') {
MathJax.Hub.Register.StartupHook('End', function () {
MathJaxListener.invokeCallbackForKey_('End');
});
}
</script>
<script type="text/javascript" src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
</div>kigihttp://www.blogger.com/profile/09881273562560954871noreply@blogger.com0tag:blogger.com,1999:blog-6312720754439261813.post-80589227619530546642014-10-01T11:54:00.000+08:002014-10-01T11:54:38.666+08:00Cassandra 簡介<div class='mymarkdown'>
<h1>Cassandra Introduction On Ubuntu</h1>
<h2>Install Ubuntu</h2>
<ul>
<li>先都用預設</li>
<li>裝 OpenSSH Server</li>
</ul>
<h2>Install JDK</h2>
<ul>
<li>sudo apt-get install openjdk-7-jdk</li>
</ul>
<p>ps. (ProcRun 用在 Windows, 不裝)</p>
<h2>Install JNA (Java Native Access)</h2>
<ul>
<li>https://github.com/twall/jna</li>
<li>sudo apt-get install libjna-java</li>
</ul>
<h2>Install Cassandra</h2>
<ul>
<li>到 http://cassandra.apache.org/download/</li>
<li>下載最新版本
<ul>
<li>wget http://apache.cdpa.nsysu.edu.tw/cassandra/2.0.6/apache-cassandra-2.0.6-bin.tar.gz</li>
</ul>
</li>
<li><p>解開剛剛下載的壓縮檔</p>
<ul>
<li>tar zxvf apache-cassandra-2.0.6-bin.tar.gz</li>
</ul>
</li>
<li><p>修改設定檔 conf/cassandra.yaml</p>
<ul>
<li>修改 cluster_name, ex: kigi-cassandra-1</li>
<li>修改 seeds 建議是兩台以上(因為 cassandra 是 P2P,需要 seed 去查所有的 nodes),只有一台,改成機器用的 IP</li>
<li>修改 listen_address, 改成機器用的 IP,或者內網的 IP, 不要用 Localhost or 127.0.0.1</li>
<li>修改 rpc_address, 同上
(p.s. seeds, listen_address, rpc_address 最好都一樣,要不然會有問題)</li>
<li><p>修改 endpoint_snitch</p>
<pre><code> endpoint_snitch: GossipingPropertyFileSnitch
</code></pre></li>
<li><p>建目錄</p>
<ul>
<li>sudo mkdir -p /var/log/cassandra</li>
<li>sudo chown -R `whoami` /var/log/cassandra</li>
<li>sudo mkdir -p /var/lib/cassandra</li>
<li>sudo chown -R `whoami` /var/lib/cassandra</li>
</ul>
</li>
</ul>
</li>
</ul>
<h2>Run Cassandra</h2>
<ul>
<li>bin/cassandra -f (前景作業) or bin/cassandra</li>
</ul>
<h2>Test</h2>
<ul>
<li>bin/cqlsh ip (因為改了 listen_address,所以預設的localhost會連不上,改連 IP)</li>
</ul>
<h2>Shutdown Cassandra</h2>
<pre><code>bin/nodetool -h ip disablegossip
bin/nodetool -h ip disablethrift
bin/nodetool -h ip drain
kill cassandra-process-id or /etc/init.d/cassandra stop if service installed
</code></pre>
<h2>Dump Data</h2>
<p><code>copy table_name to 'file_name';</code></p>
<h2>Import Data</h2>
<p><code>copy table_name from 'file_name';</code></p>
</div>kigihttp://www.blogger.com/profile/09881273562560954871noreply@blogger.com0tag:blogger.com,1999:blog-6312720754439261813.post-88885600976442417052014-09-21T17:44:00.004+08:002014-09-21T17:44:33.977+08:00Multiple Projects in Playframework<div class='mymarkdown'>
<h1>Multiple Projects in Playframework</h1>
<h2>Detail Resources</h2>
<p><a href="https://www.playframework.com/documentation/2.3.x/SBTSubProjects">Working with sub-projects</a></p>
<h2>Note</h2>
<ul>
<li><p>Suggest that creating a sub-folder in views of subproject and it can be used by <code>views.html.sub-folder.template-name(parameters)</code> like <code>views.html.admin.aa()</code></p></li>
<li><p>Subproject is part of parent</p>
<ul>
<li><code>Application.conf</code> of subproject will be a part of parnet one. Value will be relpaced with subproject.</li>
</ul>
</li>
<li><p>Subproject is not part of parent</p>
<ul>
<li>Subproject must have routes in path <code>conf</code> or custom setting in <code>Application.conf</code></li>
</ul>
</li>
</ul>
<h2>Sample</h2>
<p><a href="https://github.com/kigichang/multi-play">https://github.com/kigichang/multi-play</a></p>
</div>kigihttp://www.blogger.com/profile/09881273562560954871noreply@blogger.com0tag:blogger.com,1999:blog-6312720754439261813.post-31041905635170869782014-08-26T16:35:00.000+08:002014-08-27T14:11:20.797+08:00Scala Pattern and Extractor<div class='mymarkdown'>
<h1>Scala Pattern and Extractor</h1>
<p>一般常用的 Pattern Match</p>
<pre><code>scala> case class Test(a: Int)
defined class Test
scala> val test = Test(10)
test: Test = Test(10)
scala> test match {
| case Test(n) => println(n)
| case _ => println("not match")
| }
10
scala>
</code></pre>
<h2>Extractor</h2>
<p>一個 <em>Extractor</em> 需要有以下其中一個</p>
<ul>
<li>unapply</li>
<li>unapplySeq</li>
</ul>
<p>這類的 function ,稱為 <strong>extraction</strong>;相反的,<code>apply</code> 則稱為 <strong>injection</strong>。</p>
<ul>
<li><p>Extractor 只要有實作 <code>unapply</code> or <code>unapplySeq</code> 即可;但如果 Extractor 沒有實作 <code>apply</code>, 則 <code>unapply</code> 回傳型別必須是 <code>Boolean</code>。</p>
<p> <code>unapplySeq</code> 是用在 <strong>variable argument</strong> 也就是類似 <code>func(lst: String*)</code><del>,可以不實作 <code>apply</code> 也會有 <code>apply</code> 的效果</del>。</p></li>
<li><p>Extractor 可以是 <code>object</code> or <code>class</code>。<code>class</code> 可以存當時的條件,但 <code>object</code> 則沒有這樣的效果 (因為 object 是 singleton,無法存每次不同的比對條件)</p></li>
</ul>
<h2>Pattern, Extractor, and Binding</h2>
<ul>
<li><p>Extractor only with extraction and binding (sample from <a href="http://www.amazon.com/Programming-Scala-Comprehensive-Step-Step/dp/0981531644">Programming in Scala: A Comprehensive Step-by-Step Guide, 2nd Edition</a>)</p>
<pre><code> object EMail {
def apply(u: String, d: String) = { u + "@" + d } <-- Injection
def unapply(s: String): Option[(String, String)] = { <-- Extraction
var parts = s.split("@")
if (parts.length == 2) Some(parts(0), parts(1)) else None
}
}
object UpperCase {
def unapply(s: String): Boolean = s == s.toUpperCase() <-- Extraction Only
}
object pattern extends App {
"Test@test.com" match {
case EMail(user @ UpperCase(), domain) => println(user, domain) <-- 注意:UpperCase 後面一定要加 () (括號)
case _ => println("not match")
}
"TEST@test.com" match {
case EMail(user @ UpperCase(), domain) => println(user, domain)
case _ => println("not match")
}
}
</code></pre>
<p> <code>Test@test.com</code> 結果是 <code>not match</code>, 因為在 <code>UpperCase</code> 是 <code>false</code>. <code>TEST@test.com</code> 則是 <code>(TEST, test.com)</code></p></li>
<li><p>Extractor with variable arguement</p>
<pre><code> class Between(val min: Int, val max: Int) {
def unapplySeq(value: Int): Option[List[Int]] = <-- Extraction Only
if (min <= value && value <= max) Some(List(min, value, max))
else None
}
object pattern extends App {
val between5and15 = new Between(5, 15)
10 match {
case between5and15(min, value, max) => println(value)
case _ => println("not match")
}
20 match {
case between5and15(min, value, max) => println(value)
case _ => println("not match")
}
}
</code></pre>
<p> 因為 <code>Between</code> 的 <code>unapplySeq</code> 回傳是 <code>List(min, value, max)</code>,所以比對的 pattern 就必須是 List 的 pattern,像 <code>(min, value, max)</code> or <code>(_, value, max)</code> or <code>(min, _*)</code></p></li>
<li><p>Extractor with binding</p>
<pre><code> class Between(val min: Int, val max: Int) {
def unapplySeq(value: Int): Option[List[Int]] =
if (min <= value && value <= max) Some(List(min, value, max))
else None
}
object pattern extends App {
val between5and15 = new Between(5, 15)
(50, 10) match {
case (n @ between5and15(_*), _) => println("first match " + n)
case (_, m @ between5and15(_*)) => println("second match " + m)
case _ => println("not match")
}
}
</code></pre>
<p> <code>class</code> 的 Extractor 用在 binding 時,要注意要附上比對的 pattern (ex: <code>between5and15(_*)</code>),如果沒寫對,會比對失敗。比如說:把 <code>(_, m @ between5and15(_*))</code> 改成 <code>case (_, m @ between5and15())</code>, 雖然 m (m = 10) 在 5 ~ 15,但會比對失敗。</p></li>
</ul>
<h2>Pattern and Regex</h2>
<p>Scala 的 Regex 有實作 <code>unapplySeq</code>, Regex 搭配 Pattern 非常好用。</p>
<pre><code>val digits = """(\d+)-(\d+)""".r
"123-456" match {
case digits(a, b) => println(a, b)
case _ => println("not match")
}
"123456" match {
case digits(a, b) => println(a, b)
case _ => println("not match")
}
"abc-456" match {
case digits(a, b) => println(a, b)
case _ => println("not match")
}
</code></pre>
<p>因為 <code>digits</code> 有用到 <code>group</code>,所以 pattern 會是 <code>digits(a, b)</code>。如果把 <code>val digits = """(\d+)-(\d+)""".r</code> 改成 <code>val digits = """\d+-\d+""".r</code> 不使用 group 時,因為比對的 pattern 改變 (<code>digits(a, b)</code> -> <code>digits()</code>),所以上面的三個比對都會是 <code>not match</code>。需要將程式改成如下,才會正確</p>
<pre><code>val digits = """\d+-\d+""".r
"123-456" match {
case digits() => println("ok")
case _ => println("not match")
}
</code></pre>
<p>所以使用 <code>Regex</code> 時,儘量用 <code>group</code> 的功能,在系統設計時,彈性會比較大。</p>
<h3>Regex and Binding</h3>
<pre><code>val digits = """(\d+)-(\d+)-(\d+)""".r
("123-abc-789", "123-456-789") match {
case (_ @ digits(a, _*), _) => println(a)
case (_, _ @ digits(a, b, c)) => println(a, b, c)
case _ => println("not match")
}
</code></pre>
<p>用 Binding 時,一樣要注意比對的 pattern,如: <code>digits(a, _*)</code>, <code>digits(a, b, c)</code></p>
</div>kigihttp://www.blogger.com/profile/09881273562560954871noreply@blogger.com0tag:blogger.com,1999:blog-6312720754439261813.post-37229579931522581542014-08-11T10:50:00.001+08:002014-08-11T10:50:13.609+08:00Swift Language Guide - Inheritance<div class='mymarkdown'>
<h1>Swift Language Guide - Inheritance</h1>
<p><strong>ONLY</strong> <em>Reference Type</em> has Inheritance.</p>
<h2>Initializers</h2>
<ul>
<li><p>Subclass initializers are <strong>NOT</strong> inherited by default. So, subclass <strong>MUST</strong> invoke <em>Designated</em> initializers of superclass in its initializers.</p>
<pre><code> class Color {
let red = 0.0, green = 0.0, blue = 0.0
init(red: Double, green: Double, blue: Double) {
self.red = red
self.green = green
self.blue = blue
}
convenience init() {
self.init(red: 128.0, green: 128.0, blue: 128.0)
}
}
class AlphaColor : Color {
let alpha = 255.0
init(alpha: Double) {
super.init(red: 0.0, green: 0.0, blue: 0.0)
self.alpha = alpha
}
}
let c1 = AlphaColor(alpha: 128.0)
println("(\(c1.red), \(c1.green), \(c1.blue), \(c1.alpha))") <-- (0.0, 0.0, 0.0, 128.0)
</code></pre>
<pre><code> class Color {
let red = 0.0
var green = 0.0, blue = 0.0
init(red: Double, green: Double, blue: Double) {
self.red = red
self.green = green
self.blue = blue
}
convenience init() {
self.init(red: 128.0, green: 128.0, blue: 128.0)
}
}
class AlphaColor : Color {
let alpha = 255.0
init(alpha: Double) {
super.init() <-- Error, invoke convenience initializers of superclass
self.alpha = alpha
}
}
</code></pre>
<pre><code> class Color {
let red = 0.0
var green = 0.0, blue = 0.0
init(red: Double, green: Double, blue: Double) {
self.red = red
self.green = green
self.blue = blue
}
convenience init() {
self.init(red: 128.0, green: 128.0, blue: 128.0)
}
}
class AlphaColor : Color {
let alpha = 255.0
init(alpha: Double) {
super.init(red: 0.0, green: 0.0, blue: 0.0)
self.alpha = alpha
}
convenience init() {
super.init() <-- Error, Must invoke designated initializers of superclass
}
}
</code></pre></li>
<li><p>Subclass can <strong>NOT</strong> modify <em>Constant</em> properties of superclass.</p>
<pre><code> class Color {
let red = 0.0 <-- Constant
var green = 0.0, blue = 0.0
init(red: Double, green: Double, blue: Double) {
self.red = red
self.green = green
self.blue = blue
}
convenience init() {
self.init(red: 128.0, green: 128.0, blue: 128.0)
}
}
class AlphaColor : Color {
let alpha = 255.0
init(alpha: Double) {
super.init(red: 0.0, green: 0.0, blue: 0.0)
self.alpha = alpha
self.green = 128.0
self.red = 255.0 <-- Error, Can not modify constant properties of superclass
}
}
</code></pre></li>
</ul>
<h2>Override</h2>
<p>Use <code>override</code> to override methods, properties, and <em>Subscripts</em></p>
<ul>
<li><p>Subclass can <strong>NOT</strong> override <em>Stored</em> properties of superclass <em>Directly</em>.</p>
<pre><code> class Color {
let red = 0.0
var green = 0.0, blue = 0.0
init(red: Double, green: Double, blue: Double) {
self.red = red
self.green = green
self.blue = blue
}
convenience init() {
self.init(red: 128.0, green: 128.0, blue: 128.0)
}
}
class AlphaColor : Color {
let alpha = 255.0
override var green = 128.0 <-- Error, can not override stored properties of superclass.
init(alpha: Double) {
super.init(red: 0.0, green: 0.0, blue: 0.0)
self.alpha = alpha
self.green = 128.0
}
}
</code></pre></li>
<li><p>Subclass <strong>CAN</strong> override <em>Stored</em> properties of superclass with <em>Computed</em> ones.</p>
<pre><code> class Color {
let red = 0.0, green = 0.0, blue = 0.0
var count = 0 <-- a stored property
init(red: Double, green: Double, blue: Double) {
self.red = red
self.green = green
self.blue = blue
}
convenience init() {
self.init(red: 128.0, green: 128.0, blue: 128.0)
}
}
class AlphaColor : Color {
let alpha = 255.0
init(alpha: Double) {
super.init(red: 0.0, green: 0.0, blue: 0.0)
self.alpha = alpha
}
override var count : Int { <-- override count with computed property
get {
return super.count
}
set {
super.count = newValue
}
}
}
let c1 = AlphaColor(alpha: 128.0)
println(c1)
</code></pre></li>
<li><p>Subclass <strong>CAN</strong> override <em>Computed</em> properties of superclass.</p>
<pre><code> class Color : Printable {
let red = 0.0, green = 0.0, blue = 0.0
init(red: Double, green: Double, blue: Double) {
self.red = red
self.green = green
self.blue = blue
}
convenience init() {
self.init(red: 128.0, green: 128.0, blue: 128.0)
}
var description : String {
return "(\(red), \(green), \(blue))"
}
}
class AlphaColor : Color {
let alpha = 255.0
init(alpha: Double) {
super.init(red: 0.0, green: 0.0, blue: 0.0)
self.alpha = alpha
}
override var description : String { <-- Override computed properties.
return "(\(red), \(green), \(blue), \(alpha))"
}
}
let c1 = AlphaColor(alpha: 128.0)
println(c1)
</code></pre></li>
<li><p>Subclass can <strong>Add</strong> Property Observers to <em>Stored</em> and <em>Computed</em> properties with <strong>Override</strong> (<code>override</code>)</p>
<pre><code> class Color {
let red = 0.0, green = 0.0, blue = 0.0
var count = 0 <-- Stored Property
var count2 : Int { <-- Computed Property
get {
println("count2 getter")
return self.count
}
set {
println("count2 setter")
count = newValue
}
}
init(red: Double, green: Double, blue: Double) {
self.red = red
self.green = green
self.blue = blue
}
convenience init() {
self.init(red: 128.0, green: 128.0, blue: 128.0)
}
}
class AlphaColor : Color {
let alpha = 255.0
var cc : Int = 0
init(alpha: Double) {
super.init(red: 0.0, green: 0.0, blue: 0.0)
self.alpha = alpha
}
override var count2 : Int { <-- Add property observer to computed property
didSet {
cc += 1
println("didset: cc = \(cc)")
}
willSet {
cc += 2
println("willset: cc = \(cc)")
}
}
override var count: Int { <-- Add property observer to stored property
didSet {
println("didset: count = \(count)")
}
willSet {
println("willset: count = \(count)")
}
}
}
let c1 = AlphaColor(alpha: 128.0)
println("before setter")
c1.count2 = 20
println("after setter")
println(c1.count)
println(c1.cc)
</code></pre></li>
</ul>
<h3>Preventing Overrides</h3>
<p>Use <code>final</code> to prevent overrides, even adding property observers.</p>
<pre><code>class Color {
let red = 0.0, green = 0.0, blue = 0.0
final var count = 0 <-- Final Stored Property
final var count2 : Int { <-- Final Computed Property
get {
println("count2 getter")
return self.count
}
set {
println("count2 setter")
count = newValue
}
}
init(red: Double, green: Double, blue: Double) {
self.red = red
self.green = green
self.blue = blue
}
convenience init() {
self.init(red: 128.0, green: 128.0, blue: 128.0)
}
}
class AlphaColor : Color {
let alpha = 255.0
var cc : Int = 0
init(alpha: Double) {
super.init(red: 0.0, green: 0.0, blue: 0.0)
self.alpha = alpha
}
override var count2 : Int { <-- Error, can NOT override
didSet {
cc += 1
println("didset: cc = \(cc)")
}
willSet {
cc += 2
println("willset: cc = \(cc)")
}
}
override var count: Int { <-- Error, can NOT override
didSet {
println("didset: count = \(count)")
}
willSet {
println("willset: count = \(count)")
}
}
}
</code></pre>
</div>kigihttp://www.blogger.com/profile/09881273562560954871noreply@blogger.com0tag:blogger.com,1999:blog-6312720754439261813.post-1211235136098813192014-08-08T15:03:00.000+08:002014-08-08T15:06:53.919+08:00Swift Language Guide - Subscripts<div class='mymarkdown'>
<h1>Swift Language Guide - Subscripts</h1>
<p>Swift subscripts is like magic methods <code>__get($name)</code> and <code>__set($name, $value)</code> in PHP.</p>
<h2>Syntax</h2>
<ul>
<li>Subscripts may have <strong>MANY</strong></li>
<li>use <code>[]</code> like Array or Dictionary</li>
</ul>
<pre><code>enum Axis : Int {
case X = 0, Y, Z
}
struct Point : Printable {
var location = [Double](count: 3, repeatedValue: 0.0)
var description : String {
return "(\(location[0]), \(location[1]), \(location[2]))"
}
subscript(axis: Axis) -> Double {
get {
return location[axis.toRaw()]
}
set {
location[axis.toRaw()] = newValue
}
}
subscript(x: Axis, y: Axis) -> (Double, Double) {
get {
return (location[x.toRaw()], location[y.toRaw()])
}
set {
location[x.toRaw()] = newValue.0
location[y.toRaw()] = newValue.1
}
}
init() { }
init(x: Double, y: Double, z: Double) {
location[0] = x
location[1] = y
location[2] = z
}
}
var point = Point(x: 1.0, y: 2.0, z: 3.0)
println("point = \(point)")
println("point[x] = \(point[.X])")
point[.Y] = 5.5
println("point[y] = \(point[.Y])")
println("point = \(point)")
var (x, y) = point[.X, .Y]
println("(\(x), \(y))")
point[.Y, .Z] = (x + 20.0, y - 10.0)
println("point = \(point)")
</code></pre>
</div>kigihttp://www.blogger.com/profile/09881273562560954871noreply@blogger.com0tag:blogger.com,1999:blog-6312720754439261813.post-87759062003737461042014-08-08T14:30:00.002+08:002014-08-08T14:30:40.271+08:00Swift Language Guide - Type Properties and Methods<div class='mymarkdown'>
<h1>Swift Language Guide - Type Properties and Methods</h1>
<ul>
<li>Type properties and methods are like <code>static</code> in Java.</li>
<li>Use <code>static</code> in Value Type and <code>class</code> in Reference Type</li>
<li>Type computed property and methods can <strong>NOT</strong> use instance member data.</li>
</ul>
<h2>Type Properties</h2>
<ul>
<li><strong>Stored</strong> and <strong>Computed</strong> property belong to <strong>Value</strong> type (Structure and Enumerations)</li>
<li><strong>Reference</strong> Type (Class) only has <strong>Computed</strong> property</li>
<li><strong>Stored</strong> Type Property <strong>MUST</strong> have initial value.</li>
</ul>
<h2>Syntax</h2>
<ul>
<li><p>Enumerations</p>
<pre><code> enum TestEnum {
static var staticX = 0
static var staticY : Int {
get {
return staticX + 10
}
set {
staticX = newValue - 10
}
}
}
println(TestEnum.staticX)
println(TestEnum.staticY)
TestEnum.staticY = 30
println(TestEnum.staticX)
</code></pre></li>
<li><p>Structure</p>
<pre><code> struct TestStruct {
static var staticX = 0
static var staticY : Int {
get {
return staticX + 10
}
set {
staticX = newValue - 10
}
}
}
println(TestStruct.staticX)
println(TestStruct.staticY)
TestStruct.staticY = 50
println(TestStruct.staticX)
</code></pre></li>
<li><p>Class</p>
<pre><code> var globalX = 0
class TestClass {
class var classX : Int {
get {
return globalX + 10
}
set {
globalX = newValue - 10
}
}
}
println(TestClass.classX)
TestClass.classX = 90
println(TestClass.classX)
println(globalX)
</code></pre></li>
</ul>
<h2>Type Methods</h2>
<ul>
<li><p>Do <strong>NOT</strong> need 'mutating` when type method of value type changing type property.</p></li>
<li><p>Enumerations</p>
<pre><code> enum TestEnum {
static var staticX = 0
static var staticY : Int {
get {
return staticX + 10
}
set {
staticX = newValue - 10
}
}
static func addStaticY(y: Int) {
staticY = y
}
}
println(TestEnum.staticX)
println(TestEnum.staticY)
TestEnum.addStaticY(30)
println(TestEnum.staticX)
</code></pre></li>
<li><p>Structure</p>
<pre><code> struct TestStruct {
static var staticX = 0
static var staticY : Int {
get {
return staticX + 10
}
set {
staticX = newValue - 10
}
}
static func addStaticY(y: Int) {
staticY = y
}
}
println(TestStruct.staticX)
println(TestStruct.staticY)
TestStruct.addStaticY(50)
println(TestStruct.staticX)
</code></pre></li>
<li><p>Class</p>
<pre><code> var globalX = 0
class TestClass {
class var classX : Int {
get {
return globalX + 10
}
set {
globalX = newValue - 10
}
}
class func addClassX(x: Int) {
classX = x
}
}
println(TestClass.classX)
TestClass.addClassX(90)
println(TestClass.classX)
println(globalX)
</code></pre></li>
</ul>
</div>kigihttp://www.blogger.com/profile/09881273562560954871noreply@blogger.com0tag:blogger.com,1999:blog-6312720754439261813.post-86089964592010286312014-08-08T10:59:00.001+08:002014-08-08T15:29:20.431+08:00Swift Language Guide - Methods<div class='mymarkdown'>
<h1>Swift Language Guide - Methods</h1>
<h2>Instance Methods</h2>
<p>Instance Methods are functions in <strong>Class</strong>(<code>class</code>), <strong>Structure</strong>(<code>struct</code>) and <strong>Enumerations</strong>(<code>enum</code>).</p>
<ul>
<li>The first parameter in method is a local parameter name by default.</li>
<li>The <strong>Second</strong> and <strong>Subsequence</strong> parameters are local and <strong>External</strong> names by default.</li>
<li>Use <code>_</code> before parameter to disable <strong>External</strong> name</li>
<li><p><code>self</code> is like <code>this</code> in Java</p>
<pre><code> class Point : Printable {
var x = 0, y = 0
init(x: Int, y: Int) {
self.x = x
self.y = y
}
var description : String {
return "(\(x), \(y))"
}
func moveToX(x: Int, andY: Int) { <-- andY is an external name
self.x = x
self.y = andY
}
func moveTo(x: Int, _ y: Int) { <-- use _ to disable external name
self.x = x
self.y = y
}
}
let point = Point(x: 10, y: 20)
println(point)
point.moveToX(30, andY: 40) <-- invoke with external name
println(point)
point.moveTo(50, 60)
println(point)
</code></pre></li>
</ul>
<h3>Instance Methods in Structure and Enumerations</h3>
<p><strong>Structure</strong>(<code>struct</code>) and <strong>Enumerations</strong>(<code>enum</code>) are <strong>Value Type</strong>.</p>
<ul>
<li>Functions <strong>MUST</strong> have <code>mutating</code> when modifying properties.</li>
<li><p>Can <strong>NOT</strong> invoking a <code>mutating</code> function of a <strong>Constant</strong> structure or enumerations.</p>
<pre><code> struct Point : Printable {
var x: Double = 0.0, y: Double = 0.0
var description : String {
return "(\(x), \(y))"
}
func getX() -> Double {
return self.x
}
func getY() -> Double {
return self.y
}
mutating func moveTo(x: Double, andY y: Double) { <-- Change x and y property, so MUST have mutating.
self.x = x
self.y = y
}
func distance(that: Point) -> Double {
let dx = (self.x - that.x)
let dy = (self.y - that.y)
return sqrt(dx * dx + dy * dy)
}
}
var point1 = Point(x: 10, y: 10)
var point2 = Point(x: 20, y: 20)
println("point1 = \(point1)")
println("point2 = \(point2)")
point1.moveTo(10, andY: 20)
println("point1 = \(point1)")
println("point1.X = \(point1.x)")
println("point1.Y = \(point1.y)")
println("Distance between point1 and point2 is \(point1.distance(point2))")
let origin = Point(x: 0.0, y: 0.0) <-- Constant structure
println("origin = \(origin)")
println("Distance between point and origin is \(origin.distance(point1))")
origin.moveTo(30, andY: 40) <-- Error. Can NOT invoke a mutating function of Constant structure.
</code></pre></li>
</ul>
</div>kigihttp://www.blogger.com/profile/09881273562560954871noreply@blogger.com0tag:blogger.com,1999:blog-6312720754439261813.post-36945668788298470242014-08-07T17:24:00.002+08:002014-08-14T09:11:14.784+08:00Swift Language Guide - Properties<div class='mymarkdown'>
<h1>Swift Language Guide - Properties</h1>
<ul>
<li>Constant Value Type is mean the constant value, so you can <strong>NOT</strong> modify any properties in it.</li>
<li>Constant Reference Type is mean the constant reference value, you can <strong>NOT</strong> assign a new instance to it, but you can modify variable properties in it. The constant properties can <strong>NOT</strong> be modified as usual.</li>
</ul>
<h2>Stored Properties</h2>
<p>Stored Property is a <strong>Constant</strong> or <strong>Variable</strong> stored as part of an <strong>Instance</strong> of a particular <strong>Class</strong> or <strong>Structure</strong>.</p>
<ul>
<li>Stored Property is only in Class or Structure.</li>
<li><p>Constant stored property can be modified in <strong>Initializers</strong></p>
<pre><code> struct Point {
let x = 0, y = 0
init(x: Int, y: Int) { <-- Initializer
self.x = x
self.y = y
}
}
let point = Point(x: 30, y: 30)
</code></pre></li>
<li><p>Can <strong>NOT</strong> modify any stored properties of <del><strong>Constant Instance</strong></del> <strong>Constant Value Type</strong> (like structure), even the stored property is a <strong>Variable</strong>.</p>
<pre><code> struct Point {
var x = 0, y = 0 <-- Variable stored properties
init(x: Int, y: Int) {
self.x = x
self.y = y
}
}
let point = Point(x: 30, y: 30) <-- assign as Constant
point.x = 200 <-- Error, Can NOT change variable stored property of an Constant instance
</code></pre>
<pre><code> class Point {
var x = 0, y = 0 <-- Variable stored properties
init(x: Int, y: Int) {
self.x = x
self.y = y
}
}
let point = Point(x: 30, y: 30) <-- assign as Constant
point.x = 200 <-- OK
</code></pre></li>
</ul>
<h3>Lazy Load</h3>
<ul>
<li>Use <code>lazy</code> before declare a variable. (NOT <code>@lazy</code> in <em>The Swift Programming Lanaguage</em>. I use Xcode beta 5)</li>
<li><code>lazy</code> only apply on <strong>Variable (<code>var</code>)</strong>. <strong>Constant (<code>let</code>)</strong> will report an error.</li>
<li><code>lazy</code> is only valid for member of <strong>Structure (<code>struct</code>)</strong> or <strong>Class (<code>class</code>)</strong>.</li>
</ul>
<pre><code>struct Color : Printable {
var red = 0, green = 0, blue = 0
init(red: Int, green: Int, blue: Int) {
println("a color init with (\(red), \(green), \(blue))")
self.red = red
self.green = green
self.blue = blue
}
var description : String {
return "(\(red), \(green), \(blue))"
}
}
struct Point : Printable {
var x = 0, y = 0
lazy var color = Color(red: 128, green: 128, blue: 128) <-- a lazy member data
init(x: Int, y: Int) {
self.x = x
self.y = y
}
var description : String {
return "(\(x), \(y))"
}
}
var point = Point(x: 20, y: 20) <-- will NOT initialize color
println("\(point) color is \(point.color)") <-- Initialize color and output: a color init with (128, 128, 128)
lazy var a = { (b: Int) -> Int in <-- Error, lazy only available in structure or class.
println("aa")
return b + 10
}(20)
</code></pre>
<h2>Computed Properties</h2>
<p>Computed Property can be defined in <strong>Class (<code>class</code>)</strong>, <strong>Structures (<code>struct</code>)</strong>, and <strong>Enumerations (<code>enum</code>)</strong>.</p>
<ul>
<li>Computed Property can be defined in Class, Structure, and Enumerations.</li>
<li>Computed Property does <strong>NOT</strong> store a value actually, instead of providing a <strong>Getter</strong> and <strong>Setter</strong> (optional).</li>
<li><p>Use <code>newValue</code> for shorthand form in <strong>Setter</strong></p>
<pre><code> struct Point : Printable {
var x = 0.0, y = 0.0
var distance: Double {
get {
return sqrt(x * x + y * y)
}
set {
var tmp = sqrt(newValue * newValue / 2)
x = tmp
y = tmp
}
}
var description : String {
return "position: (\(x), \(y)) and distance: \(distance)"
}
}
var point = Point(x: 10, y: 10)
println("point is \(point)")
point.distance = 5
println("point is \(point)")
</code></pre></li>
<li><p>Computed Property <strong>MUST</strong> be a variable, <strong>NOT</strong> a constant, even if it is <strong>Read Only</strong>.</p></li>
<li><p>Read-Only computed property define <strong>Getter</strong> only, or return value without <code>get</code>.</p>
<pre><code> struct Point : Printable {
var x = 0.0, y = 0.0
var distance: Double {
get { <-- get can be ignored.
return sqrt(x * x + y * y)
}
}
var description : String {
return "position: (\(x), \(y)) and distance: \(distance)"
}
}
var point = Point(x: 10, y: 10)
println("point is \(point)")
point.distance = 5 <-- Error. Can NOT assign value
</code></pre></li>
<li><p>Computed Property on Global and Local Variables</p>
<pre><code> var x = 0, y = 0
struct Point : Printable {
var x = 0, y = 0
init() {
println("a point init with (0, 0)")
}
init(x: Int, y: Int) {
self.x = x
self.y = y
println("a point init with (\(x), \(y))")
}
var description : String {
return "(\(x), \(y))"
}
}
var a: Point {
get {
println("getter of a is called")
return Point(x: x, y: y)
}
set {
println("setter of a is called")
}
}
func testComputed() {
x = 20
y = 30
var b: Point {
get {
println("getter of b is called")
return Point(x: x, y: y)
}
set {
println("setter of b is called")
}
}
println("before printing b")
println("b is \(b)")
x = 10
y = 100
println("a is \(a)")
}
</code></pre></li>
</ul>
<h2>Property Observers</h2>
<ul>
<li><code>willSet</code> is called just before a value is stored. <code>newValue</code> is used in <code>willSet</code> implicitly.</li>
<li><code>didSet</code> is called immediately after new value is stored. <code>oldValue</code> is used in <code>didSet</code> implicitly.</li>
<li>Property Observers can <strong>NOT</strong> be used with <strong>Getter (<code>get</code>)</strong> and <strong>Setter (<code>set</code>)</strong>.</li>
<li><p>Initializers will <strong>NOT</strong> trigger <code>willSet</code> and <code>didSet</code></p>
<pre><code> struct Point : Printable {
var x: Double = 0.0 { <-- property observer with stored property
willSet {
println("a New Value (\(newValue)) will set to X")
}
didSet {
println("the Old Value of X is (\(oldValue))")
}
}
var y = 0.0
var distance: Double { <-- property observer with computed property
/* <-- willSet and didSet can NOT used with get and set
get {
return sqrt(x * x + y * y)
}
set {
var tmp = sqrt(newValue * newValue / 2)
x = tmp
y = tmp
}
*/
willSet {
println("a New Value (\(newValue)) will set to distance")
}
didSet {
println("the Old Value of distance is (\(oldValue))")
}
}
init(x: Double, y: Double) {
self.x = x
self.y = y
self.distance = sqrt(x * x + y * y)
}
var description : String {
return "position: (\(x), \(y)) and distance: \(distance)"
}
mutating func tune() {
var tmp = sqrt(distance * distance / 2)
x = tmp
y = tmp
}
}
var point = Point(x: 10.0, y: 10.0) <-- Initializers do NOT trigger observers.
println("set x")
point.x = 20.0 <-- trigger observers of x
println("set distance")
point.distance = 100 <-- trigger observers of distance
println("tune point after change distance")
point.tune() <-- trigger observers of x
</code></pre></li>
<li><p>Property Observers on Global and Local Variables</p>
<pre><code> var tmpForNew: Int = 0
var tmpForOld: Int = 0
var a: Int = 10 {
willSet {
println("the new value for a is \(newValue)")
tmpForNew = newValue
}
didSet {
println("the old value of a is \(oldValue)")
tmpForOld = oldValue
}
}
func testObservers() {
a = 30 <-- trigger observers of a
println("(\(tmpForNew), \(tmpForOld))") <-- (30, 10)
var b: Int = 20 {
willSet {
println("the new value for a is \(newValue)")
tmpForNew = newValue
}
didSet {
println("the old value of a is \(oldValue)")
tmpForOld = oldValue
}
}
b = a + b <-- trigger observers of b
println("(\(tmpForNew), \(tmpForOld))") <-- (50, 20)
}
testObservers()
</code></pre></li>
</ul>
</div>kigihttp://www.blogger.com/profile/09881273562560954871noreply@blogger.com0tag:blogger.com,1999:blog-6312720754439261813.post-49661949524842033032014-08-07T11:26:00.001+08:002014-08-14T22:05:18.962+08:00Swift Language Guide - Value and Reference Type<div class='mymarkdown'>
<h1>Swift Language Guide - Value and Reference Type</h1>
<h2>Value Type</h2>
<ul>
<li>Integer, Floating-point, Boolean (<code>Bool</code>)</li>
<li>Structure (<code>struct</code>)</li>
<li>Enumerations (<code>enum</code>)</li>
<li><strong>String</strong></li>
<li><strong>Array</strong></li>
<li><strong>Dictionary</strong></li>
</ul>
<h2>Reference Type</h2>
<ul>
<li>Class (<code>class</code>)</li>
<li>Function Type (<code>(parameter) -> (return type)</code>)</li>
<li>Closure (<code>{ (parameter) -> (return type) in statements }</code>)</li>
</ul>
<h2>Behavior</h2>
<h3>Value Type</h3>
<p>Value type is that <strong>Value</strong> is copied when it is assigned to a variable or constant, or when it is passed to a function. Therefore, we have a new <strong>Value</strong> (<strong>Instance</strong>) in memory.</p>
<p>Value type is <strong>Pass by Value</strong> when passed to functions.</p>
<ul>
<li><p><strong>Sample about String</strong></p>
<pre><code> var str = "Hello, World"
var str2 = str <-- Copy a New Value from str and assign to str2
println("str = \(str)") <-- str = Hello, World
println("str2 = \(str2)") <-- str2 = Hello, World
str.write(". abc")
println("str = \(str)") <-- str = Hello, World. abc
println("str2 = \(str2)") <-- str2 = Hello, World
</code></pre></li>
<li><p><strong>Sample about Structure</strong></p>
<pre><code> struct Point : Printable {
var x = 0, y = 0
var description : String {
return "(\(x), \(y))"
}
}
var point1 = Point(x: 10, y: 20)
var point2 = point1 <-- Copy a New Value from point1 and assign to point2
println("point1 = \(point1)") <-- point1 = (10, 20)
println("point2 = \(point2)") <-- point2 = (10, 20)
point1.x = 100
println("point1 = \(point1)") <-- point1 = (100, 20)
println("point2 = \(point2)") <-- point2 = (10, 20)
</code></pre></li>
</ul>
<h3>Reference Type</h3>
<p>Reference type is that <strong>Reference</strong> to the same instance is copied when it is assigned to a variable or constant, or when it is passed to a function. Therefore, we do <strong>NOT</strong> have new instance in memory.</p>
<p>Reference type is <strong>Pass by Value</strong> when passed to functions.</p>
<ul>
<li><p><strong>Sample about Class</strong></p>
<pre><code> class Point : Printable {
var x = 0, y = 0
init(x: Int, y: Int) {
self.x = x
self.y = y
}
var description : String {
return "(\(x), \(y))"
}
}
var point1 = Point(x: 10, y: 20)
var point2 = point1
println("point1 = \(point1)") <-- point1 = (10, 20)
println("point2 = \(point2)") <-- point2 = (10, 20)
point1.x = 100
println("point1 = \(point1)") <-- point1 = (100, 20)
println("point2 = \(point2)") <-- point2 = (100, 20)
func movePoint(point: Point) {
point.y = 200
}
movePoint(point1) <-- Copy a reference value from point1, so movePoint change y value of point1
println("point1 = \(point1)") <-- point1 = (100, 200)
println("point2 = \(point2)") <-- point2 = (100, 200)
</code></pre></li>
</ul>
<p><strong>Summary: Variable is <em>Passed by Reference</em> with <code>inout</code>.</strong></p>
<ul>
<li><p><strong>Pass By Value</strong></p>
<pre><code> struct Point : Printable {
var x = 0, y = 0
var description : String {
return "(\(x), \(y))"
}
}
class Color : Printable {
var red = 0.0, green = 0.0, blue = 0.0
var description : String {
return "(\(red), \(green), \(blue))"
}
}
var point1 = Point(x: 0, y: 0)
var point2 = point1
var color1 = Color()
var color2 = color1
func testByValue(var point: Point, var color: Color) {
point.x = 100
color = Color()
color.green = 100.0
println(color === color1) <-- false
println(color === color2) <-- false
}
println("point1 = \(point1)") <-- point1 = (0, 0)
println("point2 = \(point2)") <-- point2 = (0, 0)
println("color1 = \(color1)") <-- color1 = (0.0, 0.0, 0.0)
println("color2 = \(color2)") <-- color2 = (0.0, 0.0, 0.0)
testByValue(point1, color1)
println("point1 = \(point1)") <-- point1 = (0, 0)
println("point2 = \(point2)") <-- point2 = (0, 0)
println("color1 = \(color1)") <-- color1 = (0.0, 0.0, 0.0)
println("color2 = \(color2)") <-- color2 = (0.0, 0.0, 0.0)
</code></pre>
<p> Swift copy the reference value of color1 and assign to the parameter <code>color</code> when invoking <code>testByValue(point1, color1)</code>. We change the reference value of <code>color</code> in function <code>testByValue</code> and it does <strong>NOT</strong> affect <code>color1</code>. Because <code>color1</code> and <code>color</code> have their own copy, <code>color === color1</code> and <code>color === color2</code> are <strong>ALL FALSE</strong>.</p></li>
<li><p><strong>Pass by Reference</strong></p>
<pre><code> struct Point : Printable {
var x = 0, y = 0
var description : String {
return "(\(x), \(y))"
}
}
class Color : Printable {
var red = 0.0, green = 0.0, blue = 0.0
var description : String {
return "(\(red), \(green), \(blue))"
}
}
var point1 = Point(x: 0, y: 0)
var point2 = point1
var color1 = Color()
var color2 = color1
println("point1 = \(point1)") <-- point1 = (0, 0)
println("point2 = \(point2)") <-- point2 = (0, 0)
println("color1 = \(color1)") <-- color1 = (0.0, 0.0, 0.0)
println("color2 = \(color2)") <-- color2 = (0.0, 0.0, 0.0)
println(color1 === color2) <-- true
func testByReference(inout point: Point, inout color: Color) {
point.x = 100
color = Color()
color.green = 100.0
println(color === color1) <-- true
println(color1 === color2) <-- false
}
testByReference(&point1, &color1) <-- Pass by Reference
println("point1 = \(point1)") <-- point1 = (100, 0)
println("point2 = \(point2)") <-- point2 = (0, 0)
println("color1 = \(color1)") <-- color1 = (0.0, 100.0, 0.0)
println("color2 = \(color2)") <-- color2 = (0.0, 0.0, 0.0)
println(color1 === color2) <-- false
</code></pre>
<p> Passing by Reference with <code>inout</code> when declaring a function and <code>&</code> when invoking a function will change the value of original variable, even the reference value. <code>color1</code> is passed by reference when invoking <code>testByReference</code>. We assign New Instance to <code>color</code> and the referece value of <code>color1</code> is also changed because of passing by reference. Therefore, <code>color === color1</code> is true and <code>color1 === color2)</code> is false.</p></li>
</ul>
<p><strong>p.s. <code>===</code> is AnyObject Identity Operator. It is <code>true</code> when variables refer to the SAME instance.</strong></p>
<ul>
<li><p><strong>Arrays</strong></p>
<p> Array is <em>Value Type</em>. So, <em>Value</em> is <em>Copied</em> when it is assigned to variable or constant, or when passed to functions.</p>
<ul>
<li>The element in Array is <em>Value Type</em>, the <em>Value</em> is copied to store in Array.</li>
<li>The element in Array is <em>Reference Type</em>, the <em>Reference Value</em> is copied to store in Array.</li>
</ul>
<p> <strong>Element Behavior in Array</strong></p>
<ul>
<li><p>Value Type Elements in Array</p>
<pre><code> struct Point : Printable {
var x = 0, y = 0
var description : String {
return "(\(x), \(y))"
}
}
var points1 = [Point(x: 1, y: 1), Point(x: 2, y: 2), Point(x: 3, y: 3)]
var points2 = points1 <-- copy point1 value and assign to points2, and values of elements are copied as well.
println("point1 = \(points1[1])") <-- point1 = (2, 2)
println("point2 = \(points2[1])") <-- point2 = (2, 2)
points1[1].x = 12
println("point1 = \(points1[1])") <-- point1 = (12, 2)
println("point2 = \(points2[1])") <-- point2 = (2, 2)
points1[1] = Point(x: 22, y: 22)
println("point1 = \(points1[1])") <-- point1 = (22, 22)
println("point2 = \(points2[1])") <-- point2 = (2, 2)
points1.append(Point(x: 4, y: 4))
println(points1.count) <-- 4
println(points2.count) <-- 3
</code></pre></li>
<li><p>Reference Type Elements in Array</p>
<pre><code> class Color : Printable {
var red = 0.0, green = 0.0, blue = 0.0
init() { }
init(red: Double, green: Double, blue: Double) {
self.red = red
self.green = green
self.blue = blue
}
var description : String {
return "(\(red), \(green), \(blue))"
}
}
var colors1 = [Color(red: 255.0, green: 0.0, blue: 0.0), Color(red: 0.0, green: 255.0, blue: 0.0), Color(red: 0.0, green: 0.0, blue: 255.0)]
var colors2 = colors1 <-- copy color1 value and assign to color2, and the reference values of elements are copied as well.
println("colors1 = \(colors1[1])") <-- colors1 = (0.0, 255.0, 0.0)
println("colors2 = \(colors2[1])") <-- colors2 = (0.0, 255.0, 0.0)
colors1[1].green = 128.0
println("colors1 = \(colors1[1])") <-- colors1 = (0.0, 128.0, 0.0)
println("colors2 = \(colors2[1])") <-- colors2 = (0.0, 128.0, 0.0)
colors1[1] = Color(red: 128.0, green: 128.0, blue: 128.0)
println("colors1 = \(colors1[1])") <-- colors1 = (128.0, 128.0, 128.0)
println("colors2 = \(colors2[1])") <-- colors2 = (0.0, 128.0, 0.0)
colors1.append(Color(red: 255.0, green: 255.0, blue: 255.0))
println(colors1.count) <-- 4
println(colors2.count) <-- 3
</code></pre></li>
</ul>
</li>
</ul>
<p><strong>Note: The behavior of Array is NOT the same as <em>The Swift Programming Language</em>. I use XCode beta 5. </strong></p>
<ul>
<li><p><strong>Dictionary</strong></p>
<p> Dictionary is the <strong>SAME</strong> as Array.</p>
<p> <strong>Value Behavior in Dictionary</strong></p>
<ul>
<li><p>Value Type Value in Dictionary</p>
<pre><code> struct Point : Printable {
var x = 0, y = 0
var description: String {
return "(\(self.x), \(self.y))"
}
}
class Color : Printable {
var red = 0.0, green = 0.0, blue = 0.0
init() { }
init(red: Double, green: Double, blue: Double) {
self.red = red
self.green = green
self.blue = blue
}
var description: String {
return "(\(red), \(green), \(blue))"
}
}
func dumpDict<T, U>(dict: [T:U]) {
for (key, value) in dict {
print("(\(key), \(value)) ")
}
println()
}
var dict1 = ["zero" : Point(x: 0, y: 0), "one" : Point(x: 1, y: 1)]
var dict2 = dict1
dumpDict(dict1) <-- (one, (1, 1)) (zero, (0, 0))
dumpDict(dict2) <-- (one, (1, 1)) (zero, (0, 0))
dict1["zero"] = Point(x: 0, y: -1)
dumpDict(dict1) <-- (one, (1, 1)) (zero, (0, -1))
dumpDict(dict2) <-- (one, (1, 1)) (zero, (0, 0))
var point = dict1["one"]! <-- Copy a new Value for assignment
point.x = 200 <-- Change x value of New Copy
dumpDict(dict1) <-- (one, (1, 1)) (zero, (0, -1)) (No Effect)
dumpDict(dict2) <-- (one, (1, 1)) (zero, (0, 0))
</code></pre></li>
<li><p>Reference Type Value in Dictionary</p>
<pre><code> struct Point : Printable {
var x = 0, y = 0
var description: String {
return "(\(self.x), \(self.y))"
}
}
class Color : Printable {
var red = 0.0, green = 0.0, blue = 0.0
init() { }
init(red: Double, green: Double, blue: Double) {
self.red = red
self.green = green
self.blue = blue
}
var description: String {
return "(\(red), \(green), \(blue))"
}
}
func dumpDict<T, U>(dict: [T:U]) {
for (key, value) in dict {
print("(\(key), \(value)) ")
}
println()
}
var dict1 = ["white": Color(red: 255.0, green: 255.0, blue: 255.0)]
var dict2 = dict1
dumpDict(dict1) <-- (white, (255.0, 255.0, 255.0))
dumpDict(dict2) <-- (white, (255.0, 255.0, 255.0))
var color = dict1["white"]! <-- Copy Reference Value for assignment
color.red = 128.0 <-- change red value of the referred Instance
dumpDict(dict1) <-- (white, (128.0, 255.0, 255.0)) (red value changed)
dumpDict(dict2) <-- (white, (128.0, 255.0, 255.0)) (red value changed)
dict1["white"] = Color(red: 128.0, green: 128.0, blue: 128.0) <-- Copy a the Reference Value of the New Color to dict1["white"]
dumpDict(dict1) <-- (white, (128.0, 128.0, 128.0)) <-- the new color value
dumpDict(dict2) <-- (white, (128.0, 255.0, 255.0)) <-- keep the old reference value
</code></pre></li>
</ul>
</li>
</ul>
</div>kigihttp://www.blogger.com/profile/09881273562560954871noreply@blogger.com0