├── .github └── FUNDING.yml ├── .gitignore ├── LICENSE ├── README.md ├── bg.rakuguide.adoc ├── de.rakuguide.adoc ├── es.rakuguide.adoc ├── fr.rakuguide.adoc ├── id.rakuguide.adoc ├── it.rakuguide.adoc ├── ja.rakuguide.adoc ├── nl.rakuguide.adoc ├── pt.rakuguide.adoc ├── rakuguide.adoc ├── ru.rakuguide.adoc ├── tr.rakuguide.adoc ├── uk.rakuguide.adoc └── zh.rakuguide.adoc /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: hankache 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | *.pdf 3 | .vscode -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Attribution-ShareAlike 4.0 International 2 | 3 | ======================================================================= 4 | 5 | Creative Commons Corporation ("Creative Commons") is not a law firm and 6 | does not provide legal services or legal advice. Distribution of 7 | Creative Commons public licenses does not create a lawyer-client or 8 | other relationship. Creative Commons makes its licenses and related 9 | information available on an "as-is" basis. Creative Commons gives no 10 | warranties regarding its licenses, any material licensed under their 11 | terms and conditions, or any related information. Creative Commons 12 | disclaims all liability for damages resulting from their use to the 13 | fullest extent possible. 14 | 15 | Using Creative Commons Public Licenses 16 | 17 | Creative Commons public licenses provide a standard set of terms and 18 | conditions that creators and other rights holders may use to share 19 | original works of authorship and other material subject to copyright 20 | and certain other rights specified in the public license below. The 21 | following considerations are for informational purposes only, are not 22 | exhaustive, and do not form part of our licenses. 23 | 24 | Considerations for licensors: Our public licenses are 25 | intended for use by those authorized to give the public 26 | permission to use material in ways otherwise restricted by 27 | copyright and certain other rights. Our licenses are 28 | irrevocable. Licensors should read and understand the terms 29 | and conditions of the license they choose before applying it. 30 | Licensors should also secure all rights necessary before 31 | applying our licenses so that the public can reuse the 32 | material as expected. Licensors should clearly mark any 33 | material not subject to the license. This includes other CC- 34 | licensed material, or material used under an exception or 35 | limitation to copyright. More considerations for licensors: 36 | wiki.creativecommons.org/Considerations_for_licensors 37 | 38 | Considerations for the public: By using one of our public 39 | licenses, a licensor grants the public permission to use the 40 | licensed material under specified terms and conditions. If 41 | the licensor's permission is not necessary for any reason--for 42 | example, because of any applicable exception or limitation to 43 | copyright--then that use is not regulated by the license. Our 44 | licenses grant only permissions under copyright and certain 45 | other rights that a licensor has authority to grant. Use of 46 | the licensed material may still be restricted for other 47 | reasons, including because others have copyright or other 48 | rights in the material. A licensor may make special requests, 49 | such as asking that all changes be marked or described. 50 | Although not required by our licenses, you are encouraged to 51 | respect those requests where reasonable. More_considerations 52 | for the public: 53 | wiki.creativecommons.org/Considerations_for_licensees 54 | 55 | ======================================================================= 56 | 57 | Creative Commons Attribution-ShareAlike 4.0 International Public 58 | License 59 | 60 | By exercising the Licensed Rights (defined below), You accept and agree 61 | to be bound by the terms and conditions of this Creative Commons 62 | Attribution-ShareAlike 4.0 International Public License ("Public 63 | License"). To the extent this Public License may be interpreted as a 64 | contract, You are granted the Licensed Rights in consideration of Your 65 | acceptance of these terms and conditions, and the Licensor grants You 66 | such rights in consideration of benefits the Licensor receives from 67 | making the Licensed Material available under these terms and 68 | conditions. 69 | 70 | 71 | Section 1 -- Definitions. 72 | 73 | a. Adapted Material means material subject to Copyright and Similar 74 | Rights that is derived from or based upon the Licensed Material 75 | and in which the Licensed Material is translated, altered, 76 | arranged, transformed, or otherwise modified in a manner requiring 77 | permission under the Copyright and Similar Rights held by the 78 | Licensor. For purposes of this Public License, where the Licensed 79 | Material is a musical work, performance, or sound recording, 80 | Adapted Material is always produced where the Licensed Material is 81 | synched in timed relation with a moving image. 82 | 83 | b. Adapter's License means the license You apply to Your Copyright 84 | and Similar Rights in Your contributions to Adapted Material in 85 | accordance with the terms and conditions of this Public License. 86 | 87 | c. BY-SA Compatible License means a license listed at 88 | creativecommons.org/compatiblelicenses, approved by Creative 89 | Commons as essentially the equivalent of this Public License. 90 | 91 | d. Copyright and Similar Rights means copyright and/or similar rights 92 | closely related to copyright including, without limitation, 93 | performance, broadcast, sound recording, and Sui Generis Database 94 | Rights, without regard to how the rights are labeled or 95 | categorized. For purposes of this Public License, the rights 96 | specified in Section 2(b)(1)-(2) are not Copyright and Similar 97 | Rights. 98 | 99 | e. Effective Technological Measures means those measures that, in the 100 | absence of proper authority, may not be circumvented under laws 101 | fulfilling obligations under Article 11 of the WIPO Copyright 102 | Treaty adopted on December 20, 1996, and/or similar international 103 | agreements. 104 | 105 | f. Exceptions and Limitations means fair use, fair dealing, and/or 106 | any other exception or limitation to Copyright and Similar Rights 107 | that applies to Your use of the Licensed Material. 108 | 109 | g. License Elements means the license attributes listed in the name 110 | of a Creative Commons Public License. The License Elements of this 111 | Public License are Attribution and ShareAlike. 112 | 113 | h. Licensed Material means the artistic or literary work, database, 114 | or other material to which the Licensor applied this Public 115 | License. 116 | 117 | i. Licensed Rights means the rights granted to You subject to the 118 | terms and conditions of this Public License, which are limited to 119 | all Copyright and Similar Rights that apply to Your use of the 120 | Licensed Material and that the Licensor has authority to license. 121 | 122 | j. Licensor means the individual(s) or entity(ies) granting rights 123 | under this Public License. 124 | 125 | k. Share means to provide material to the public by any means or 126 | process that requires permission under the Licensed Rights, such 127 | as reproduction, public display, public performance, distribution, 128 | dissemination, communication, or importation, and to make material 129 | available to the public including in ways that members of the 130 | public may access the material from a place and at a time 131 | individually chosen by them. 132 | 133 | l. Sui Generis Database Rights means rights other than copyright 134 | resulting from Directive 96/9/EC of the European Parliament and of 135 | the Council of 11 March 1996 on the legal protection of databases, 136 | as amended and/or succeeded, as well as other essentially 137 | equivalent rights anywhere in the world. 138 | 139 | m. You means the individual or entity exercising the Licensed Rights 140 | under this Public License. Your has a corresponding meaning. 141 | 142 | 143 | Section 2 -- Scope. 144 | 145 | a. License grant. 146 | 147 | 1. Subject to the terms and conditions of this Public License, 148 | the Licensor hereby grants You a worldwide, royalty-free, 149 | non-sublicensable, non-exclusive, irrevocable license to 150 | exercise the Licensed Rights in the Licensed Material to: 151 | 152 | a. reproduce and Share the Licensed Material, in whole or 153 | in part; and 154 | 155 | b. produce, reproduce, and Share Adapted Material. 156 | 157 | 2. Exceptions and Limitations. For the avoidance of doubt, where 158 | Exceptions and Limitations apply to Your use, this Public 159 | License does not apply, and You do not need to comply with 160 | its terms and conditions. 161 | 162 | 3. Term. The term of this Public License is specified in Section 163 | 6(a). 164 | 165 | 4. Media and formats; technical modifications allowed. The 166 | Licensor authorizes You to exercise the Licensed Rights in 167 | all media and formats whether now known or hereafter created, 168 | and to make technical modifications necessary to do so. The 169 | Licensor waives and/or agrees not to assert any right or 170 | authority to forbid You from making technical modifications 171 | necessary to exercise the Licensed Rights, including 172 | technical modifications necessary to circumvent Effective 173 | Technological Measures. For purposes of this Public License, 174 | simply making modifications authorized by this Section 2(a) 175 | (4) never produces Adapted Material. 176 | 177 | 5. Downstream recipients. 178 | 179 | a. Offer from the Licensor -- Licensed Material. Every 180 | recipient of the Licensed Material automatically 181 | receives an offer from the Licensor to exercise the 182 | Licensed Rights under the terms and conditions of this 183 | Public License. 184 | 185 | b. Additional offer from the Licensor -- Adapted Material. 186 | Every recipient of Adapted Material from You 187 | automatically receives an offer from the Licensor to 188 | exercise the Licensed Rights in the Adapted Material 189 | under the conditions of the Adapter's License You apply. 190 | 191 | c. No downstream restrictions. You may not offer or impose 192 | any additional or different terms or conditions on, or 193 | apply any Effective Technological Measures to, the 194 | Licensed Material if doing so restricts exercise of the 195 | Licensed Rights by any recipient of the Licensed 196 | Material. 197 | 198 | 6. No endorsement. Nothing in this Public License constitutes or 199 | may be construed as permission to assert or imply that You 200 | are, or that Your use of the Licensed Material is, connected 201 | with, or sponsored, endorsed, or granted official status by, 202 | the Licensor or others designated to receive attribution as 203 | provided in Section 3(a)(1)(A)(i). 204 | 205 | b. Other rights. 206 | 207 | 1. Moral rights, such as the right of integrity, are not 208 | licensed under this Public License, nor are publicity, 209 | privacy, and/or other similar personality rights; however, to 210 | the extent possible, the Licensor waives and/or agrees not to 211 | assert any such rights held by the Licensor to the limited 212 | extent necessary to allow You to exercise the Licensed 213 | Rights, but not otherwise. 214 | 215 | 2. Patent and trademark rights are not licensed under this 216 | Public License. 217 | 218 | 3. To the extent possible, the Licensor waives any right to 219 | collect royalties from You for the exercise of the Licensed 220 | Rights, whether directly or through a collecting society 221 | under any voluntary or waivable statutory or compulsory 222 | licensing scheme. In all other cases the Licensor expressly 223 | reserves any right to collect such royalties. 224 | 225 | 226 | Section 3 -- License Conditions. 227 | 228 | Your exercise of the Licensed Rights is expressly made subject to the 229 | following conditions. 230 | 231 | a. Attribution. 232 | 233 | 1. If You Share the Licensed Material (including in modified 234 | form), You must: 235 | 236 | a. retain the following if it is supplied by the Licensor 237 | with the Licensed Material: 238 | 239 | i. identification of the creator(s) of the Licensed 240 | Material and any others designated to receive 241 | attribution, in any reasonable manner requested by 242 | the Licensor (including by pseudonym if 243 | designated); 244 | 245 | ii. a copyright notice; 246 | 247 | iii. a notice that refers to this Public License; 248 | 249 | iv. a notice that refers to the disclaimer of 250 | warranties; 251 | 252 | v. a URI or hyperlink to the Licensed Material to the 253 | extent reasonably practicable; 254 | 255 | b. indicate if You modified the Licensed Material and 256 | retain an indication of any previous modifications; and 257 | 258 | c. indicate the Licensed Material is licensed under this 259 | Public License, and include the text of, or the URI or 260 | hyperlink to, this Public License. 261 | 262 | 2. You may satisfy the conditions in Section 3(a)(1) in any 263 | reasonable manner based on the medium, means, and context in 264 | which You Share the Licensed Material. For example, it may be 265 | reasonable to satisfy the conditions by providing a URI or 266 | hyperlink to a resource that includes the required 267 | information. 268 | 269 | 3. If requested by the Licensor, You must remove any of the 270 | information required by Section 3(a)(1)(A) to the extent 271 | reasonably practicable. 272 | 273 | b. ShareAlike. 274 | 275 | In addition to the conditions in Section 3(a), if You Share 276 | Adapted Material You produce, the following conditions also apply. 277 | 278 | 1. The Adapter's License You apply must be a Creative Commons 279 | license with the same License Elements, this version or 280 | later, or a BY-SA Compatible License. 281 | 282 | 2. You must include the text of, or the URI or hyperlink to, the 283 | Adapter's License You apply. You may satisfy this condition 284 | in any reasonable manner based on the medium, means, and 285 | context in which You Share Adapted Material. 286 | 287 | 3. You may not offer or impose any additional or different terms 288 | or conditions on, or apply any Effective Technological 289 | Measures to, Adapted Material that restrict exercise of the 290 | rights granted under the Adapter's License You apply. 291 | 292 | 293 | Section 4 -- Sui Generis Database Rights. 294 | 295 | Where the Licensed Rights include Sui Generis Database Rights that 296 | apply to Your use of the Licensed Material: 297 | 298 | a. for the avoidance of doubt, Section 2(a)(1) grants You the right 299 | to extract, reuse, reproduce, and Share all or a substantial 300 | portion of the contents of the database; 301 | 302 | b. if You include all or a substantial portion of the database 303 | contents in a database in which You have Sui Generis Database 304 | Rights, then the database in which You have Sui Generis Database 305 | Rights (but not its individual contents) is Adapted Material, 306 | 307 | including for purposes of Section 3(b); and 308 | c. You must comply with the conditions in Section 3(a) if You Share 309 | all or a substantial portion of the contents of the database. 310 | 311 | For the avoidance of doubt, this Section 4 supplements and does not 312 | replace Your obligations under this Public License where the Licensed 313 | Rights include other Copyright and Similar Rights. 314 | 315 | 316 | Section 5 -- Disclaimer of Warranties and Limitation of Liability. 317 | 318 | a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE 319 | EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS 320 | AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF 321 | ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, 322 | IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, 323 | WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR 324 | PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, 325 | ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT 326 | KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT 327 | ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. 328 | 329 | b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE 330 | TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, 331 | NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, 332 | INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, 333 | COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR 334 | USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN 335 | ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR 336 | DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR 337 | IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. 338 | 339 | c. The disclaimer of warranties and limitation of liability provided 340 | above shall be interpreted in a manner that, to the extent 341 | possible, most closely approximates an absolute disclaimer and 342 | waiver of all liability. 343 | 344 | 345 | Section 6 -- Term and Termination. 346 | 347 | a. This Public License applies for the term of the Copyright and 348 | Similar Rights licensed here. However, if You fail to comply with 349 | this Public License, then Your rights under this Public License 350 | terminate automatically. 351 | 352 | b. Where Your right to use the Licensed Material has terminated under 353 | Section 6(a), it reinstates: 354 | 355 | 1. automatically as of the date the violation is cured, provided 356 | it is cured within 30 days of Your discovery of the 357 | violation; or 358 | 359 | 2. upon express reinstatement by the Licensor. 360 | 361 | For the avoidance of doubt, this Section 6(b) does not affect any 362 | right the Licensor may have to seek remedies for Your violations 363 | of this Public License. 364 | 365 | c. For the avoidance of doubt, the Licensor may also offer the 366 | Licensed Material under separate terms or conditions or stop 367 | distributing the Licensed Material at any time; however, doing so 368 | will not terminate this Public License. 369 | 370 | d. Sections 1, 5, 6, 7, and 8 survive termination of this Public 371 | License. 372 | 373 | 374 | Section 7 -- Other Terms and Conditions. 375 | 376 | a. The Licensor shall not be bound by any additional or different 377 | terms or conditions communicated by You unless expressly agreed. 378 | 379 | b. Any arrangements, understandings, or agreements regarding the 380 | Licensed Material not stated herein are separate from and 381 | independent of the terms and conditions of this Public License. 382 | 383 | 384 | Section 8 -- Interpretation. 385 | 386 | a. For the avoidance of doubt, this Public License does not, and 387 | shall not be interpreted to, reduce, limit, restrict, or impose 388 | conditions on any use of the Licensed Material that could lawfully 389 | be made without permission under this Public License. 390 | 391 | b. To the extent possible, if any provision of this Public License is 392 | deemed unenforceable, it shall be automatically reformed to the 393 | minimum extent necessary to make it enforceable. If the provision 394 | cannot be reformed, it shall be severed from this Public License 395 | without affecting the enforceability of the remaining terms and 396 | conditions. 397 | 398 | c. No term or condition of this Public License will be waived and no 399 | failure to comply consented to unless expressly agreed to by the 400 | Licensor. 401 | 402 | d. Nothing in this Public License constitutes or may be interpreted 403 | as a limitation upon, or waiver of, any privileges and immunities 404 | that apply to the Licensor or You, including from the legal 405 | processes of any jurisdiction or authority. 406 | 407 | 408 | ======================================================================= 409 | 410 | Creative Commons is not a party to its public 411 | licenses. Notwithstanding, Creative Commons may elect to apply one of 412 | its public licenses to material it publishes and in those instances 413 | will be considered the “Licensor.” The text of the Creative Commons 414 | public licenses is dedicated to the public domain under the CC0 Public 415 | Domain Dedication. Except for the limited purpose of indicating that 416 | material is shared under a Creative Commons public license or as 417 | otherwise permitted by the Creative Commons policies published at 418 | creativecommons.org/policies, Creative Commons does not authorize the 419 | use of the trademark "Creative Commons" or any other trademark or logo 420 | of Creative Commons without its prior written consent including, 421 | without limitation, in connection with any unauthorized modifications 422 | to any of its public licenses or any other arrangements, 423 | understandings, or agreements concerning use of licensed material. For 424 | the avoidance of doubt, this paragraph does not form part of the 425 | public licenses. 426 | 427 | Creative Commons may be contacted at creativecommons.org. 428 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Raku Guide 2 | 3 | This document is intended to give you a quick overview of the Raku programming language. 4 | For those who are new to Raku it should get you up and running. 5 | 6 | ### Website 7 | For online reading navigate to: 8 | * English: https://raku.guide 9 | * French: https://raku.guide/fr 10 | * German: https://raku.guide/de 11 | * Japanese: https://raku.guide/ja 12 | * Spanish: https://raku.guide/es 13 | * Portuguese: https://raku.guide/pt 14 | * Dutch: https://raku.guide/nl 15 | * Bulgarian: https://raku.guide/bg 16 | * Chinese: https://raku.guide/zh 17 | * Italian: https://raku.guide/it 18 | * Turkish: https://raku.guide/tr 19 | * Indonesian: https://raku.guide/id 20 | * Russian: https://raku.guide/ru 21 | * Ukrainian: https://raku.guide/uk 22 | 23 | ### Building the document 24 | The document is written in asciidoc format and generated using 25 | asciidoctor and pygments. You will need a current version of **ruby**, 26 | **asciidoctor**, **pygments**, and a special gem that provides a pre-release version 27 | of **asciidoctor-pdf**. 28 | 29 | Install the required tools: 30 | 31 | $ sudo pip install Pygments 32 | $ sudo gem install asciidoctor 33 | $ sudo gem install asciidoctor-pdf 34 | $ sudo gem install pygments.rb 35 | 36 | To produce **rakuguide.pdf**, run: 37 | 38 | $ asciidoctor-pdf rakuguide.adoc 39 | 40 | To produce **rakuguide.html**, run: 41 | 42 | $ asciidoctor rakuguide.adoc 43 | 44 | ### Feedback 45 | All feedback is welcomed: 46 | * Corrections 47 | * Suggestions 48 | * Additions 49 | * Translations 50 | 51 | ### Translations 52 | If you wish to translate this document, always use the English version as your starting point. 53 | If you are starting a new translation create a new file. For example, the French translation will be in fr.rakuguide.adoc, the Deutsch translation in de.rakuguide.adoc 54 | If you want to modify a translated version, consider modifying the English version first. It is important that all translations be kept in sync. 55 | 56 | Currently the translations are in different phases of completion. For completeness rely on the English version. 57 | 58 | ### Contributing 59 | Kindly prefix your commit title with the language it is targeting. For example, all commits targeting the English version would have a title that starts with [EN]. All commits targeting the Spanish translation have a title that starts with [ES]. 60 | 61 | ### Authors 62 | * Original English version: [Naoum Hankache](https://github.com/hankache) 63 | * French Translation: [Romuald Nuguet](https://github.com/kolikov) 64 | * German Translation: Sören Laird Sörries 65 | * Japanese Translation: [Itsuki Toyota](https://github.com/titsuki) 66 | * Spanish Translation: [Ramiro Encinas](https://github.com/ramiroencinas) 67 | * Portuguese Translation: [Breno G. de Oliveira](https://github.com/garu) 68 | * Dutch Translation: [Elizabeth Mattijsen](https://github.com/lizmat) 69 | * Bulgarian Translation: [Красимир Беров](https://github.com/kberov) 70 | * Chinese Translation: [wenjie1991](https://github.com/wenjie1991) and [ohmycloud](https://ohmycloud.github.io) 71 | * Italian Translation: [MarsMarsico](https://github.com/marsmarsico) 72 | * Turkish Translation: [Yalın Pala](https://github.com/yplog) 73 | * Indonesian Translation: [Heince Kurniawan](https://github.com/heince) 74 | * Russian Translation: [Alexander Kiryuhin](https://github.com/Altai-man) 75 | * Ukrainian Translation: [Dmytro Iaskolko](https://github.com/s0t0na) 76 | 77 | For the full list of contributors: https://github.com/hankache/rakuguide/graphs/contributors 78 | 79 | ### License 80 | Creative Commons Attribution-ShareAlike 4.0 International License. 81 | To view a copy of this license, visit https://creativecommons.org/licenses/by-sa/4.0/. 82 | -------------------------------------------------------------------------------- /ja.rakuguide.adoc: -------------------------------------------------------------------------------- 1 | = Raku 入門 2 | Naoum Hankache ; Itsuki Toyota 3 | :description: Raku 入門 4 | :keywords: perl6, perl 6, introduction, イントロダクション, perl6intro, perl 6 introduction, perl 6 tutorial, perl 6 intro, perl 6 入門, perl 6 イントロダクション, perl 6 イントロ, raku イントロダクション, raku ガイド, raku チュートリアル, raku 入門 5 | :Revision: 1.0 6 | :icons: font 7 | :source-highlighter: pygments 8 | //:pygments-style: manni 9 | :source-language: perl6 10 | :pygments-linenums-mode: table 11 | :toc: left 12 | :toc-title: 目次 13 | :doctype: book 14 | :lang: ja 15 | 16 | この文書はプログラミング言語Rakuの全体像を素早くつかんでもらうことを目的として書かれたものです。 + 17 | まだRakuを触ったことのない読者の方々が、ここからRakuをはじめてもらうことを狙いとしています。 18 | 19 | この文書のいくつかの節では https://docs.raku.org[Rakuの公式文書] における(もっと完成されていて正確な)箇所を参照しています。 20 | 21 | 特定の事項に関してもっと情報がほしいのであればそれらの箇所を読んでみることをおすすめします。 22 | 23 | この文書の中では、ほとんどのトピックにおいて例がふんだんに用いられています。 24 | 理解を深めるために、自分でもすべての例を再現してみることをおすすめします。 25 | 26 | .ライセンス 27 | This work is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. 28 | To view a copy of this license, visit 29 | 30 | * https://creativecommons.org/licenses/by-sa/4.0/. 31 | 32 | .貢献 33 | もしこの文書に貢献したいのなら次のURLへ向かってください: 34 | 35 | * https://github.com/hankache/rakuguide 36 | 37 | .フィードバック 38 | フィードバックは大歓迎です!: 39 | 40 | * naoum@hankache.com 41 | 42 | * titsuki@cpan.org 43 | 44 | もし、このRaku入門を気に入ったのなら、 _Star_ を押していただければ幸いです。 45 | link:https://github.com/hankache/rakuguide[Github]. 46 | 47 | :sectnums: 48 | == イントロダクション 49 | === Raku とは 50 | Rakuは高水準、汎用、漸進的型付けの言語です。 51 | Rakuはマルチパラダイム言語です。手続き型、オブジェクト指向、関数型プログラミングをサポートしています。 52 | 53 | .Raku のモットー: 54 | * TMTOWTDI (ティムトゥディ と発音します): やり方はひとつじゃない 55 | 56 | === 専門用語 57 | * *Raku* : はテストスイートもあわせての言語の仕様です。 58 | 仕様に基づいたテストスイートを通るような実装はRakuと考えられます。 59 | * *Rakudo* : はRakuのためのコンパイラです。 60 | * *Zef* : はRakuのモジュールのインストーラーです。 61 | * *Rakudo Star*: はRakudo, Zef, Rakuのモジュールのコレクション, 文書を含んだバンドルソフトです。 62 | 63 | === Rakuのインストール 64 | .Linux 65 | Rakudo Starをインストールするには、次のコマンドをターミナルで実行してください: 66 | ---- 67 | mkdir ~/rakudo && cd $_ 68 | curl -LJO https://rakudo.org/latest/star/src 69 | tar -xzf rakudo-star-*.tar.gz 70 | mv rakudo-star-*/* . 71 | rm -fr rakudo-star-* 72 | 73 | ./bin/rstar install 74 | 75 | echo "export PATH=$(pwd)/bin/:$(pwd)/share/perl6/site/bin:$(pwd)/share/perl6/vendor/bin:$(pwd)/share/perl6/core/bin:\$PATH" >> ~/.bashrc 76 | source ~/.bashrc 77 | ---- 78 | 79 | 他のインストール方法を知りたければ次のリンクを参照してください。 https://rakudo.org/star/source 80 | 81 | .macOS 82 | 四つの選択肢から選んでください: 83 | 84 | * Linuxにおけるインストール手順と同じステップを踏む 85 | * homebrewによるインストール: `brew install rakudo-star` 86 | * MacPortsによるインストール: `sudo port install rakudo` 87 | * 次のURLから最新のインストーラー(.dmg拡張子のついたファイル)をダウンロードする https://rakudo.perl6.org/downloads/star/ 88 | 89 | .Windows 90 | . 64bit版の場合: 最新のインストーラー(.msi拡張子のついたファイル)を次からダウンロードする https://rakudo.org/latest/star/win 91 | . インストール後 `C:\rakudo\bin` がPATH変数に含まれていることを確認してください。 92 | 93 | .Docker 94 | . 次のコマンドで公式のDockerイメージを入手してください `docker pull rakudo-star` 95 | . イメージを含んだコンテナを実行するために次のコマンドを打ってください `docker run -it rakudo-star` 96 | 97 | === Rakuのコードを実行する 98 | 99 | Rakuのコードの実行はREPL (Read-Eval-Print Loop)を用いることによって行うことができます。 100 | この実行を行うために、ターミナルを開き、ターミナルの窓に向かって `perl6` と打ち、[Enter]ボタンを押してください。 101 | そうすると、コマンドプロンプトから `>` が表示されるはずです。 102 | 次に、コードの行を打って[Enter]を押してください。 103 | REPL はこの行の値を出力するでしょう。 104 | そうしたら、次のコードの行を打つか、 `exit` と打った後[Enter]を押すことでREPLを去るか、どちらでも選ぶことができます。 105 | 106 | あるいは、ファイルの中にコードを書いて、保存して実行してください。 107 | Rakuのスクリプトは `.raku` 拡張子を持つことが推奨されています。 108 | ターミナルの窓に対して `perl6 filename.raku` と打ち、[Enter]を押してください。 109 | REPLとは違って、それぞれの行の結果が自動的に出力されるでしょう。: 出力を行うには、 `say` のような命令が含まれている必要があります。 110 | 111 | REPLは多くの場合、特定のコードを実行するために用いられ、そのコードは一般的には一行です。 112 | 一行以上のプログラムに対しては、ファイルに保存してから実行することをおすすめします。 113 | 114 | 一行のコードはコマンドラインで非対話的に実行することもできます。 115 | `perl6 -e 'あなたの書いたコード'` と打ち [Enter]を押してください. 116 | 117 | [TIP] 118 | -- 119 | Rakudo StarはREPLを使い倒すためのラインエディタをバンドルしています。 120 | 121 | もしRakudo Starではなく無印のRakudoをインストールしたのなら、行編集機能を有効化(履歴閲覧のための上矢印キーと下矢印キーの使用、入力編集のための左矢印キーと右矢印キーの使用、タブによる補完機能)していないはずです。 122 | 次のコマンドを実行してこれらの機能を有効化することを考えてみてください。: 123 | 124 | * `zef install Linenoise` Windows/Linux/macOSで動きます 125 | 126 | * `zef install Readline` もしLinux環境で _Readline_ ライブラリを使いたいのであれば 127 | -- 128 | 129 | === エディタ 130 | ほとんどの場合、Rakuのプログラムを書いて保存することになります。 131 | そのため、Rakuの文法を認識できるまともなテキストエディタを持っているべきです。 132 | 133 | 私が個人的に使っていておすすめのエディタは https://atom.io/[Atom] です。 134 | モダンなテキストエディタであり、革新的なRakuのシンタックスハイライティング機能を持っています。 135 | 別の選択肢として、 https://atom.io/packages/language-perl6[Perl 6 FE] というAtomのためのシンタックスハイライターがあります。 136 | オリジナルのパッケージから派生したものですが、多くのバグフィックスと追加機能を含んでいます。 137 | 138 | http://www.vim.org/[Vim], https://www.gnu.org/software/emacs/[Emacs], http://padre.perlide.org/[Padre] もまた、Rakuコミュニティの他の人々によって使われています。 139 | 140 | 最近のバージョンのVimは、はじめから革新的なシンタックスハイライティング機能を持っています。 141 | EmacsとPadreは追加のパッケージのインストールが必要になるでしょう。 142 | 143 | === こんにちは世界! 144 | おなじみの `こんにちは世界` の儀式をはじめましょう。 145 | 146 | [source,perl6] 147 | say 'こんにちは世界'; 148 | 149 | これはこういう風に書くこともできます: 150 | 151 | [source,perl6] 152 | 'こんにちは世界'.say; 153 | 154 | === 文法の概要 155 | Raku は *自由形式*: ほとんどの場合、空白文字をどれだけの量つかっても良いです。ただし、空白文字が意味を持つ場合もあります。 156 | 157 | *命令文* は一般的にはコードの論理的な行です。最後にセミコロンがついている必要があります: 158 | `say "Hello" if True;` 159 | 160 | *式* は値を返すような特殊なタイプの命令文です: 161 | `1+2` は `3` を返すでしょう。 162 | 式は *項* と *演算子* でできています。 163 | 164 | *項* は: 165 | 166 | * *変数*: 操作したり変更したりできる値です。 167 | 168 | * *リテラル*: 数や文字列のような定数です。 169 | 170 | *演算子* は次のような種類に分類されます。: 171 | 172 | |=== 173 | 174 | | *種類* | *説明* | *例* 175 | 176 | | 接頭辞 | 項の前 | `++1` 177 | 178 | | 接中辞 | 項の間 | `1+2` 179 | 180 | | 接尾辞 | 項の後ろ | `1++` 181 | 182 | | 接周辞 | 項の周り | `(1)` 183 | 184 | | 後置接周辞 | 項の後ろの、また別の項の周り | `Array[1]` 185 | 186 | |=== 187 | 188 | ==== 識別子 189 | 識別子とは項を定義したときに与えられる名前のことです。 190 | 191 | .ルール: 192 | * アルファベットかアンダースコアで始まっていなければならない。 193 | 194 | * 数字をふくむことができる。(ただし先頭文字は除く) 195 | 196 | * アルファベットがダッシュやアポストロフィ(ただし最初と最後の文字は除く)の右側にあるなら、ダッシュやアポストロフィをふくむことができる。 197 | 198 | |=== 199 | 200 | | *正しい例* | *間違った例* 201 | 202 | | `var1` | `1var` 203 | 204 | | `var-one` | `var-1` 205 | 206 | | `var'one` | `var'1` 207 | 208 | | `var1_` | `var1'` 209 | 210 | | `_var` | `-var` 211 | 212 | |=== 213 | 214 | .命名規則: 215 | * キャメルケース: `variableNo1` 216 | 217 | * ケバブケース: `variable-no1` 218 | 219 | * スネークケース: `variable_no1` 220 | 221 | 識別子には好きなように名前をつけることができます。しかし、一貫して一つの命名規則を適用していくのがグッドプラクティスです。 222 | 223 | ちゃんと意味のある名前をつければプログラミング人生を楽なものにしてくれるかもしれません。 224 | 225 | * `var1 = var2 * var3` は文法的には正しいですが目的が明白ではありません。 226 | * `monthly-salary = daily-rate * working-days` のほうが変数名としてふさわしいでしょう。 227 | 228 | ==== コメント 229 | コメントはコンパイラーに無視され注釈として使われるテキストです。 230 | 231 | コメントは三つのタイプに分けられます: 232 | 233 | * 一行: 234 | + 235 | [source,perl6] 236 | # これは一行のコメントです 237 | 238 | * 埋め込み: 239 | + 240 | [source,perl6] 241 | say #`(これは埋めこまれたコメントです) "Hello World." 242 | 243 | * 複数行: 244 | + 245 | [source,perl6] 246 | ---- 247 | =begin comment 248 | これは複数行のコメントです。 249 | コメント1 250 | コメント2 251 | =end comment 252 | ---- 253 | 254 | ==== クォート 255 | 256 | 文字列はダブルクォートかシングルクォートのどちらかで囲まれていなければなりません。 257 | 258 | 下記に該当する場合は常にダブルクォートを使うべきです: 259 | 260 | * 文字列がアポストロフィを含んでいる 261 | 262 | * 文字列が展開される必要のある変数を含んでいる 263 | 264 | [source,perl6] 265 | ---- 266 | say 'Hello World'; # Hello World 267 | say "Hello World"; # Hello World 268 | say "Don't"; # Don't 269 | my $name = 'John Doe'; 270 | say 'Hello $name'; # Hello $name 271 | say "Hello $name"; # Hello John Doe 272 | ---- 273 | 274 | == 演算子 275 | 276 | === 一般的な演算子 277 | 278 | 下記の表は最も一般的に使われている演算子を掲載しています。 279 | [cols="^.^5m,^.^5m,.^20,.^20m,.^20m", options="header"] 280 | |=== 281 | 282 | | 演算子 | 種類 | 説明 | 例 | 結果 283 | 284 | | + | 接中辞 | 加算 | 1 + 2 | 3 285 | 286 | | - | 接中辞 | 減算 | 3 - 1 | 2 287 | 288 | | * | 接中辞 | 乗算 | 3 * 2 | 6 289 | 290 | | ** | 接中辞 | 冪乗 | 3 ** 2 | 9 291 | 292 | | / | 接中辞 | 除算 | 3 / 2 | 1.5 293 | 294 | | div | 接中辞 | 整数除算 (切り捨て) | 3 div 2 | 1 295 | 296 | | % | 接中辞 | 法 | 7 % 4 | 3 297 | 298 | .2+| %% .2+| 接中辞 .2+| 割り切れるか否か | 6 %% 4 | False 299 | 300 | <| 6 %% 3 <| True 301 | 302 | | gcd | 接中辞 | 最大公約数 | 6 gcd 9 | 3 303 | 304 | | lcm | 接中辞 | 最小公倍数 | 6 lcm 9 | 18 305 | 306 | | == | 接中辞 | 数値が等しい | 9 == 7 | False 307 | 308 | | != | 接中辞 | 数値が等しくない | 9 != 7 | True 309 | 310 | | < | 接中辞 | 数値が小さい | 9 < 7 | False 311 | 312 | | > | 接中辞 | 数値が大きい | 9 > 7 | True 313 | 314 | | \<= | 接中辞 | 数値が等しいか小さい | 7 \<= 7 | True 315 | 316 | | >= | 接中辞 | 数値が等しいか大きい | 9 >= 7 | True 317 | 318 | .3+| +<=>+ .3+| 接中辞 .3+| 数値の三元演算子 | 1 +<=>+ 1.0 | Same 319 | 320 | <| 1 +<=>+ 2 <| Less 321 | 322 | <| 3 +<=> 2+ <| More 323 | 324 | | eq | 接中辞 | 文字列が等しい | "John" eq "John" | True 325 | 326 | | ne | 接中辞 | 文字列が等しくない | "John" ne "Jane" | True 327 | 328 | | lt | 接中辞 | 辞書順で小さい | "a" lt "b" | True 329 | 330 | | gt | 接中辞 | 辞書順で大きい | "a" gt "b" | False 331 | 332 | | le | 接中辞 | 辞書順で小さいか等しい | "a" le "a" | True 333 | 334 | | ge | 接中辞 | 辞書順で大きいか等しい | "a" ge "b" | False 335 | 336 | .3+| leg .3+| 接中辞 .3+| 文字列の三元演算子 | "a" leg "a" | Same 337 | 338 | <| "a" leg "b" <| Less 339 | 340 | <| "c" leg "b" <| More 341 | 342 | .2+| cmp .2+| Infix .2+| スマート三元演算子 | "a" cmp "b" | Less 343 | 344 | <| 3.5 cmp 2.6 <| More 345 | 346 | | = | 接中辞 | 代入 | my $var = 7 | 変数 `$var` に値 `7` を代入する 347 | 348 | .2+| ~ .2+| 接中辞 .2+| 文字列の結合 | 9 ~ 7 | 97 349 | 350 | 'London', Germany => 'Berlin'; 657 | say %capitals; 658 | ---- 659 | 660 | ハッシュに対して呼び出すことのできるいくつかのメソッド: 661 | [source,perl6] 662 | .`スクリプト` 663 | ---- 664 | my %capitals = UK => 'London', Germany => 'Berlin'; 665 | %capitals.push: (France => 'Paris'); 666 | say %capitals.kv; 667 | say %capitals.keys; 668 | say %capitals.values; 669 | say "The capital of France is: " ~ %capitals; 670 | ---- 671 | 672 | .`出力` 673 | ---- 674 | (France Paris Germany Berlin UK London) 675 | (France Germany UK) 676 | (Paris Berlin London) 677 | The capital of France is: Paris 678 | ---- 679 | 680 | .説明 681 | `.push: (key \=> 'Value')` は新たな キー/値のペアを追加します。 + 682 | `.kv` はすべてのキーと値を含んだリストを返します。 + 683 | `.keys` はすべてのキーを含んだリストを返します。 + 684 | `.values` はすべての値を含んだリストを返します。 + 685 | キーを指定することでハッシュの中の特定の値にアクセスすることができます。`%hash` 686 | 687 | NOTE: もしハッシュに関するすべての情報を知りたいのであれば、次のURLを参照することをすすめます https://docs.raku.org/type/Hash 688 | 689 | === 型 690 | 今までの例では、変数が保持しているべき値の型について指定してはいませんでした。 691 | 692 | TIP: `.WHAT` は変数が保持している値の型を返します。 693 | 694 | [source,perl6] 695 | ---- 696 | my $var = 'Text'; 697 | say $var; 698 | say $var.WHAT; 699 | 700 | $var = 123; 701 | say $var; 702 | say $var.WHAT; 703 | ---- 704 | 705 | 上記の例からわかるように、 `$var` の中の値の型は一度 (Str) になり、それから (Int) になっています。 706 | 707 | このプログラミングのスタイルは動的型付けと呼ばれています。 708 | 変数はAny型の値を持つことができるという意味で動的なのです。 709 | 710 | では、下記の例を実行してみましょう: + 711 | 変数名の前の `Int` に注目してください。 712 | 713 | [source,perl6] 714 | ---- 715 | my Int $var = 'Text'; 716 | say $var; 717 | say $var.WHAT; 718 | ---- 719 | 720 | これは実行に失敗して次のようなメッセージを返すでしょう: `Type check failed in assignment to $var; expected Int but got Str` 721 | 722 | あらかじめ変数の型は(Int)でなければならないと指定したのが原因です。 723 | この変数に対して(Str)型の値を代入しようとしたときに、失敗してしまいました。 724 | 725 | このプログラミングのスタイルは静的型付けとよばれています。 726 | 変数の型は代入の前に定義され、変えることができないという意味で静的なのです。 727 | 728 | Rakuは *漸進的型付け* に分類されます; *静的* 型付けと *動的* 型付けの両方を使うことができるのです。 729 | 730 | .配列とハッシュもまた静的型付けを行うことができます: 731 | [source,perl6] 732 | ---- 733 | my Int @array = 1,2,3; 734 | say @array; 735 | say @array.WHAT; 736 | 737 | my Str @multilingual = "Hello","Salut","Hallo","您好","안녕하세요","こんにちは"; 738 | say @multilingual; 739 | say @multilingual.WHAT; 740 | 741 | my Str %capitals = UK => 'London', Germany => 'Berlin'; 742 | say %capitals; 743 | say %capitals.WHAT; 744 | 745 | my Int %country-codes = UK => 44, Germany => 49; 746 | say %country-codes; 747 | say %country-codes.WHAT; 748 | ---- 749 | 750 | .下記は最も一般的に使われている型のリストです: 751 | 最初の二つの型は使わないかもしれませんが情報提供のために掲載しておきます。 752 | 753 | [cols="^.^1m,.^3m,.^2m,.^1m, options="header"] 754 | |=== 755 | 756 | | *型* | *説明* | *例* | *結果* 757 | 758 | | Mu | Rakuの型階層のルート | | 759 | 760 | | Any | 新しいクラスやほとんどの組み込みのクラスのためのデフォルトの基底クラス | | 761 | 762 | | Cool | 文字列と数値を交互に扱うことのできる値 | my Cool $var = 31; say $var.flip; say $var * 2; | 13 62 763 | 764 | | Str | 文字列 | my Str $var = "NEON"; say $var.flip; | NOEN 765 | 766 | | Int | 整数 (任意の精度) | 7 + 7 | 14 767 | 768 | | Rat | 有理数 (制限された精度) | 0.1 + 0.2 | 0.3 769 | 770 | | Bool | ブーリアン | !True | False 771 | 772 | |=== 773 | 774 | === イントロスペクション 775 | 776 | イントロスペクションはその型といったオブジェクトのプロパティについて情報を得るための処理です。 + 777 | 前節の例の一つでは変数の型を返すために `.WHAT` を使いました。 778 | 779 | [source,perl6] 780 | ---- 781 | my Int $var; 782 | say $var.WHAT; # (Int) 783 | my $var2; 784 | say $var2.WHAT; # (Any) 785 | $var2 = 1; 786 | say $var2.WHAT; # (Int) 787 | $var2 = "Hello"; 788 | say $var2.WHAT; # (Str) 789 | $var2 = True; 790 | say $var2.WHAT; # (Bool) 791 | $var2 = Nil; 792 | say $var2.WHAT; # (Any) 793 | ---- 794 | 795 | 値を持っている変数の型はその値と相互に関連があります。 + 796 | 型の指定された空の変数の型はその変数が宣言されたときの型です。 + 797 | 型の指定されていない空の変数の型は `(Any)` です。 + 798 | 変数の値をクリアするためには `Nil` を代入してください。 799 | 800 | === スコーピング 801 | 初めに変数を使う前に、宣言されている必要があります。 802 | 803 | 様々な宣言子がRakuでは使われています。今までのところ、使ってきたのは `my` です。 804 | 805 | [source,perl6] 806 | my $var=1; 807 | 808 | `my` 宣言子は変数に対して *レキシカル* スコープを与えます。 809 | つまり、その変数はそれが宣言されたのと同じブロックの中でしかアクセスできなくなります。 810 | 811 | 812 | Rakuのブロックは `{ }` で囲まれます。 813 | もしブロックがみつからないのなら、その変数はRakuのスクリプト全体で使える状態になっています。 814 | 815 | [source,perl6] 816 | ---- 817 | { 818 | my Str $var = 'Text'; 819 | say $var; # はアクセス可能です 820 | } 821 | say $var; # はアクセス可能ではありません。エラーを返します。 822 | ---- 823 | 824 | 変数はその変数が定義されたブロックの中でのみアクセスが可能なので、別のブロックでは同じ変数名で使用できます。 825 | 826 | [source,perl6] 827 | ---- 828 | { 829 | my Str $var = 'Text'; 830 | say $var; 831 | } 832 | my Int $var = 123; 833 | say $var; 834 | ---- 835 | 836 | === 代入と束縛 837 | 前節の例では、どうやって変数に値を *代入* するかをみてきました。 + 838 | *代入* は `=` 演算子を用いて行われます。 839 | [source,perl6] 840 | ---- 841 | my Int $var = 123; 842 | say $var; 843 | ---- 844 | 845 | 変数に代入された値は変更することができます: 846 | 847 | [source,perl6] 848 | .代入 849 | ---- 850 | my Int $var = 123; 851 | say $var; 852 | $var = 999; 853 | say $var; 854 | ---- 855 | 856 | .`出力` 857 | ---- 858 | 123 859 | 999 860 | ---- 861 | 862 | 一方、変数に *束縛* された値は変えることはできません。 + 863 | *束縛* は `:=` 演算子を用いて行われます。 864 | 865 | [source,perl6] 866 | .束縛 867 | ---- 868 | my Int $var := 123; 869 | say $var; 870 | $var = 999; 871 | say $var; 872 | ---- 873 | 874 | .`出力` 875 | ---- 876 | 123 877 | Cannot assign to an immutable value 878 | ---- 879 | 880 | [source,perl6] 881 | .変数はほかの変数により束縛することができます: 882 | ---- 883 | my $a; 884 | my $b; 885 | $b := $a; 886 | $a = 7; 887 | say $b; 888 | $b = 8; 889 | say $a; 890 | ---- 891 | 892 | .`出力` 893 | ---- 894 | 7 895 | 8 896 | ---- 897 | 898 | 束縛変数は双方向性をもっています。 + 899 | `$a := $b` と `$b := $a` は同じ効果を持っています。 900 | 901 | NOTE: もし変数に関する情報をもっと知りたいのであれば、次のURLを参照することをすすめます https://docs.raku.org/type/variables 902 | 903 | == 関数とミューテータ 904 | 905 | 関数とミューテータを区別することは重要です。 + 906 | 関数はそれが呼ばれたときのオブジェクトの状態を変更しません。 + 907 | ミューテータはオブジェクトの状態を変更します。 908 | 909 | [source,perl6,linenums] 910 | .`スクリプト` 911 | ---- 912 | my @numbers = [7,2,4,9,11,3]; 913 | 914 | @numbers.push(99); 915 | say @numbers; #1 916 | 917 | say @numbers.sort; #2 918 | say @numbers; #3 919 | 920 | @numbers.=sort; 921 | say @numbers; #4 922 | ---- 923 | 924 | .`出力` 925 | ---- 926 | [7 2 4 9 11 3 99] #1 927 | (2 3 4 7 9 11 99) #2 928 | [7 2 4 9 11 3 99] #3 929 | [2 3 4 7 9 11 99] #4 930 | ---- 931 | 932 | .Explanation 933 | `.push` はミューテータです。配列の状態を変更します。 (#1) 934 | 935 | `.sort` は関数です。ソートされた配列を返しますが、配列の初期状態を変更するわけではありません。: 936 | 937 | * (#2) はソートされた配列が返されたことを示しています。 938 | 939 | * (#3) は元々の配列は変更されていないことを示しています。 940 | 941 | 関数にミューテータとしてふるまうように強制するために `.` のかわりに `.=` を使っています。(#4) (スクリプトの9行目) 942 | 943 | == ループと条件文 944 | Rakuは多数の条件文とループ構文を持っています。 945 | 946 | === if 947 | 条件が満たされた時、つまり式が `True` と評価された時だけコードが実行されます。 948 | 949 | [source,perl6] 950 | ---- 951 | my $age = 19; 952 | 953 | if $age > 18 { 954 | say 'Welcome' 955 | } 956 | ---- 957 | 958 | Rakuでは、コードと条件は反転することができます。 + 959 | コードと条件が反転されていたとしても、いつも先に条件が評価されます。 960 | 961 | [source,perl6] 962 | ---- 963 | my $age = 19; 964 | 965 | say 'Welcome' if $age > 18; 966 | ---- 967 | 968 | もし条件が満たされないなら、下記を使って、別のブロックを実行するように指定することができます: 969 | 970 | * `else` 971 | * `elsif` 972 | 973 | [source,perl6] 974 | ---- 975 | # 変数の値を変えて同じコードを実行 976 | my $number-of-seats = 9; 977 | 978 | if $number-of-seats <= 5 { 979 | say 'I am a sedan' 980 | } elsif $number-of-seats <= 7 { 981 | say 'I am 7 seater' 982 | } else { 983 | say 'I am a van' 984 | } 985 | ---- 986 | 987 | === unless 988 | `unless` を使えば、if命令の否定版を書くことができます。 989 | 990 | 次のコード: 991 | 992 | [source,perl6] 993 | ---- 994 | my $clean-shoes = False; 995 | 996 | if not $clean-shoes { 997 | say 'Clean your shoes' 998 | } 999 | ---- 1000 | は次のように書くことができます: 1001 | 1002 | [source,perl6] 1003 | ---- 1004 | my $clean-shoes = False; 1005 | 1006 | unless $clean-shoes { 1007 | say 'Clean your shoes' 1008 | } 1009 | ---- 1010 | 1011 | Rakuでの否定は `!` か `not` を使って行われます。 1012 | 1013 | `unless (condition)` は `if not (condition)` のかわりに用いられます。 1014 | 1015 | `unless` は `else` 節を持つことができません。 1016 | 1017 | === with 1018 | 1019 | `with` は `if` 命令のようにふるまいます。しかし変数が定義されているかどうかを調べます。 1020 | 1021 | [source,perl6] 1022 | ---- 1023 | my Int $var=1; 1024 | 1025 | with $var { 1026 | say 'Hello' 1027 | } 1028 | ---- 1029 | 1030 | 変数に対して値を代入しないでコードを実行した場合何も起こらないでしょう。 1031 | [source,perl6] 1032 | ---- 1033 | my Int $var; 1034 | 1035 | with $var { 1036 | say 'Hello' 1037 | } 1038 | ---- 1039 | 1040 | `without` は `with` の否定版です。 `unless` と関連づけて覚えておくとよいでしょう。 1041 | 1042 | もしはじめの `with` 条件が満たされないなら、 `orwith` を使って代替となるパスを指定することができます。 + 1043 | `with` と `orwith` の関係性は `if` と `elsif` の関係性にたとえることができます。 1044 | 1045 | === for 1046 | 1047 | `for` ループは複数の値に対する反復処理を行うことができます。 1048 | 1049 | [source,perl6] 1050 | ---- 1051 | my @array = 1,2,3; 1052 | 1053 | for @array -> $array-item { 1054 | say $array-item * 100 1055 | } 1056 | ---- 1057 | 1058 | 反復変数 `$array-item` を作り、配列のそれぞれの要素に対して `*100` の操作を行ったことに注目してください。 1059 | 1060 | === given 1061 | 1062 | `given` は他の言語においてswitch命令と呼ばれているものと等価なRakuの命令です。 1063 | しかし、他の言語よりも強力です。 1064 | 1065 | [source,perl6] 1066 | ---- 1067 | my $var = 42; 1068 | 1069 | given $var { 1070 | when 0..50 { say 'Less than or equal to 50'} 1071 | when Int { say "is an Int" } 1072 | when 42 { say 42 } 1073 | default { say "huh?" } 1074 | } 1075 | ---- 1076 | 1077 | 条件が満たされると、条件が満たされるかどうか調べる処理は止まります。 1078 | 1079 | 別の選択肢として、 `proceed` を使うと、Rakuは条件が満たされた後も、この調べる処理の実行を続けます。 1080 | [source,perl6] 1081 | ---- 1082 | my $var = 42; 1083 | 1084 | given $var { 1085 | when 0..50 { say 'Less than or equal to 50';proceed} 1086 | when Int { say "is an Int";proceed} 1087 | when 42 { say 42 } 1088 | default { say "huh?" } 1089 | } 1090 | ---- 1091 | 1092 | === loop 1093 | 1094 | `loop` は `for` を書くための別の選択肢です。 1095 | 1096 | 実際に、 `loop` はC言語族において書かれる `for` と同じです。 1097 | 1098 | RakuはC言語族の一員なのです。 1099 | 1100 | [source,perl6] 1101 | ---- 1102 | loop (my $i = 0; $i < 5; $i++) { 1103 | say "The current number is $i" 1104 | } 1105 | ---- 1106 | 1107 | NOTE: もしループ構文と条件文に関する情報をもっと知りたいのであれば、次のURLを参照することをすすめます https://docs.raku.org/language/control 1108 | 1109 | == I/O 1110 | Rakuにおいて、二つの最も一般的な _入力/出力_ のインタフェースは _ターミナル_ と _ファイル_ です。 1111 | 1112 | === ターミナルを用いた基本的なI/O 1113 | 1114 | ==== say 1115 | `say` は標準出力に対する書き込みを行います。その最後に改行を付加します。つまり、次のようなコードは: 1116 | 1117 | [source,perl6] 1118 | ---- 1119 | say 'Hello Mam.'; 1120 | say 'Hello Sir.'; 1121 | ---- 1122 | 二つの行に分かれて書き込まれることになります。 1123 | 1124 | ==== print 1125 | 一方、 `print` は `say` のようにふるまいますが、改行を付加しないという違いがあります。 1126 | 1127 | `say` を `print` で置き換えてみて結果を比べてみましょう。 1128 | 1129 | ==== get 1130 | `get` はターミナルからの入力を取得するために使われます。 1131 | 1132 | [source,perl6] 1133 | ---- 1134 | my $name; 1135 | 1136 | say "Hi, what's your name?"; 1137 | $name = get; 1138 | 1139 | say "Dear $name welcome to Raku"; 1140 | ---- 1141 | 1142 | 上記のコードが実行されると、ターミナルは、あなたが名前を入力するのを待つようになります。名前を打って [Enter] を押しましょう。 1143 | その後、あいさつをしてくれるでしょう。 1144 | 1145 | ==== prompt 1146 | `prompt` は `print` と `get` の組み合わせです。 1147 | 1148 | 上の例は次のように書くことができます: 1149 | 1150 | [source,perl6] 1151 | ---- 1152 | my $name = prompt "Hi, what's your name? "; 1153 | 1154 | say "Dear $name welcome to Raku"; 1155 | ---- 1156 | 1157 | === シェルコマンドの実行 1158 | 二つのサブルーチンをシェルコマンドを実行するために使うことができます: 1159 | 1160 | * `run` シェルを介在せずに外部コマンドを実行します 1161 | 1162 | * `shell` システムシェルを通じてコマンドを実行します。プラットフォームとシェル依存です。 1163 | すべてのシェルのメタ文字はシェルによって解釈されます。これには、パイプ、リダイレクト、ユーザー環境変数などが含まれます。 1164 | 1165 | [source,perl6] 1166 | .もし Linux/macOS 環境にいるなら、次のコードを実行してください。 1167 | ---- 1168 | my $name = 'Neo'; 1169 | run 'echo', "hello $name"; 1170 | shell "ls"; 1171 | ---- 1172 | 1173 | [source,perl6] 1174 | .もし Windows 環境にいるなら、次のコードを実行してください。 1175 | ---- 1176 | shell "dir"; 1177 | ---- 1178 | `echo` と `ls` はLinuxにおける一般的なシェルのキーワードです。: + 1179 | `echo` はターミナルにテキストを出力します。 (Rakuにおける `say` と等価です。) + 1180 | `ls` はカレントディレクトリのすべてのファイルとフォルダを表示します。 1181 | 1182 | `dir` はWindowsにおける `ls` と等価なキーワードです。 1183 | 1184 | === ファイル I/O 1185 | ==== slurp 1186 | `slurp` はファイルからデータを読み込むために使われます。 1187 | 1188 | 次のような内容のテキストファイルを作ってください: 1189 | 1190 | .datafile.txt 1191 | ---- 1192 | John 9 1193 | Johnnie 7 1194 | Jane 8 1195 | Joanna 7 1196 | ---- 1197 | [source,perl6] 1198 | ---- 1199 | my $data = slurp "datafile.txt"; 1200 | say $data; 1201 | ---- 1202 | 1203 | ==== spurt 1204 | `spurt` はデータをファイルに書き込むために使われます。 1205 | 1206 | [source,perl6] 1207 | ---- 1208 | my $newdata = "New scores: 1209 | Paul 10 1210 | Paulie 9 1211 | Paulo 11"; 1212 | 1213 | spurt "newdatafile.txt", $newdata; 1214 | ---- 1215 | 1216 | 上記のコードの実行後、 _newdatafile.txt_ という名前の新しいファイルが作られるはずです。 1217 | このファイルは新しいスコアを含んでいるでしょう。 1218 | 1219 | === ファイルとディレクトリの操作 1220 | Rakuはシェルコマンドに頼らなくてもディレクトリの内容を表示することができます( シェルコマンドの例としては `ls` が挙げられます )。 1221 | 1222 | [source,perl6] 1223 | ---- 1224 | say dir; # カレントディレクトリのファイルとフォルダを表示する 1225 | say dir "/Documents"; # 指定されたディレクトリのファイルとフォルダを表示する 1226 | ---- 1227 | 1228 | 加えて、新しいディレクトリを作ったり削除したりすることもできます。 1229 | 1230 | [source,perl6] 1231 | ---- 1232 | mkdir "newfolder"; 1233 | rmdir "newfolder"; 1234 | ---- 1235 | 1236 | `mkdir` は新しいディレクトリをつくります + 1237 | `rmdir` は空のディレクトリを削除し、もし空でないのならエラーを返します。 1238 | 1239 | ファイルかディレクトリであれば、パスが存在するかどうか確かめることもできます。: 1240 | 1241 | 下記のスクリプトを実行するディレクトリで、 `folder123` という空のフォルダと `script123.raku` という空のrakuファイルを生成してください。 1242 | 1243 | [source,perl6] 1244 | ---- 1245 | say "script123.raku".IO.e; 1246 | say "folder123".IO.e; 1247 | 1248 | say "script123.raku".IO.d; 1249 | say "folder123".IO.d; 1250 | 1251 | say "script123.raku".IO.f; 1252 | say "folder123".IO.f; 1253 | ---- 1254 | 1255 | `IO.e` はディレクトリ/ファイルが存在するかどうか調べます。 + 1256 | `IO.f` はパスがファイルかどうか調べます。 + 1257 | `IO.d` はパスがディレクトリかどうか調べます。 1258 | 1259 | WARNING: Windowsのユーザーはディレクトリを定義するために `/` か `\\` を使うことができます + 1260 | `C:\\rakudo\\bin` + 1261 | `C:/rakudo/bin` + 1262 | 1263 | NOTE: もしI/Oに関する情報をもっと知りたいのであれば、次のURLを参照することをすすめます https://docs.raku.org/type/IO 1264 | 1265 | == サブルーチン 1266 | === 定義 1267 | *サブルーチン* (*サブ* や *関数* とも呼ばれます) は機能のパッケージングと再利用の手段です + 1268 | 1269 | サブルーチンの定義は `sub` というキーワードから始まります。定義の後、つけた名前を使って呼び出すことができます。 + 1270 | 下記の例をよく見てください: 1271 | 1272 | [source,perl6] 1273 | ---- 1274 | sub alien-greeting { 1275 | say "Hello earthlings"; 1276 | } 1277 | 1278 | alien-greeting; 1279 | ---- 1280 | 1281 | 先ほどの例では、入力を必要としないサブルーチンを紹介しました。 1282 | 1283 | === シグネチャ 1284 | サブルーチンは入力を必要とする場合があります。この時の入力は *引数* によって与えられます。 1285 | サブルーチンはゼロかそれ以上の *パラメータ* を定義します。 1286 | サブルーチンが定義するパラメータの数と型は *シグネチャ* と呼ばれています。 1287 | 1288 | 下記のサブルーチンは引数として文字列を受け取っています。 1289 | 1290 | [source,perl6] 1291 | ---- 1292 | sub say-hello (Str $name) { 1293 | say "Hello " ~ $name ~ "!!!!" 1294 | } 1295 | say-hello "Paul"; 1296 | say-hello "Paula"; 1297 | ---- 1298 | 1299 | === 多重ディスパッチ 1300 | 同じ名前を持っているが異なったシグネチャを持つように複数のサブルーチンを定義することができます。 1301 | サブルーチンが呼ばれると、ランタイム環境は与えられた引数の数と型をもとにしてどれを使うべきであるか決定します。 1302 | このタイプのサブルーチンは普通のサブルーチンと同じように定義できます。ただし、このとき `sub` のかわりに `multi` というキーワードを用います。 1303 | 1304 | [source,perl6] 1305 | ---- 1306 | multi greet($name) { 1307 | say "Good morning $name"; 1308 | } 1309 | multi greet($name, $title) { 1310 | say "Good morning $title $name"; 1311 | } 1312 | 1313 | greet "Johnnie"; 1314 | greet "Laura","Mrs."; 1315 | ---- 1316 | 1317 | === デフォルトパラメータとオプションパラメータ 1318 | もしサブルーチンが一つの引数を受け取るように定義されていて、必要となる引数が与えられずに呼び出されたのなら、このサブルーチンの実行は失敗します。 1319 | 1320 | Rakuでは次のような引数をともなったサブルーチンを定義することができます: 1321 | 1322 | * オプションパラメータ 1323 | * デフォルトパラメータ 1324 | 1325 | オプションパラメータはパラメータの名前に対して `?` を付加することで定義できます。 1326 | 1327 | [source,perl6] 1328 | ---- 1329 | sub say-hello($name?) { 1330 | with $name { say "Hello " ~ $name } 1331 | else { say "Hello Human" } 1332 | } 1333 | say-hello; 1334 | say-hello("Laura"); 1335 | ---- 1336 | 1337 | もしユーザーが引数を与える必要がないのであれば、デフォルト値を定義することができます。 + 1338 | これは、サブルーチンの定義内でパラメータに対して値を代入することで行うことができます。 1339 | 1340 | [source,perl6] 1341 | ---- 1342 | sub say-hello($name="Matt") { 1343 | say "Hello " ~ $name; 1344 | } 1345 | say-hello; 1346 | say-hello("Laura"); 1347 | ---- 1348 | 1349 | === 値の返却 1350 | 1351 | 今まで見てきたすべてのサブルーチンは、ターミナルにテキストを出力するといった具合に *何かをするもの* でした 。 1352 | でも時には、プログラムの後段の処理で利用できるような何らかの値を *返して* もらうためにサブルーチンを実行することもあるでしょう。 1353 | 1354 | 1355 | もし関数がそのブロックの最後まで実行されるなら、最後の文や式が返り値を決定するでしょう。 1356 | [source,perl6] 1357 | .暗黙的な返却 1358 | ---- 1359 | sub squared ($x) { 1360 | $x ** 2; 1361 | } 1362 | say "7 squared is equal to " ~ squared(7); 1363 | ---- 1364 | 1365 | コードが大きくなってしまったなら _明示的に_ 何を返そうとしているのかを指定することは良い対処法かもしれません。 1366 | `return` キーワードを用いることでこれを行うことができます。 1367 | [source,perl6] 1368 | .明示的な返却 1369 | ---- 1370 | sub squared ($x) { 1371 | return $x ** 2; 1372 | } 1373 | say "7 squared is equal to " ~ squared(7); 1374 | ---- 1375 | ==== 返り値の制限 1376 | 以前の例の一つでは、どうやって引数の受け取る型をある型に制限するかについて見ました。 1377 | 同じことを返り値でも行うことができます。 1378 | 1379 | 返り値をある型に制限するには、矢印記号 `-\->` をシグネチャで使ってください。 1380 | 1381 | [source,perl6] 1382 | .返り値の型を示す 1383 | ---- 1384 | sub squared ($x --> Int) { 1385 | return $x ** 2; 1386 | } 1387 | say "1.2 squared is equal to " ~ squared(1.2); 1388 | ---- 1389 | もし、型の制限にマッチする返り値を渡しそびれてしまったなら、エラーが投げられるでしょう。 1390 | 1391 | ---- 1392 | Type check failed for return value; expected Int but got Rat (1.44) 1393 | ---- 1394 | 1395 | [TIP] 1396 | ==== 1397 | 型の制限は返り値の型を指定できるだけではなく; 定義済みか否かも指定することができます。 1398 | 1399 | 以前の例では、返り値は `Int` であると指定しました。 1400 | 次のようなシグネチャを使って返り値の `Int` が厳密に定義されているべきか否かを指定することもできます。: 1401 | 1402 | `--> Int:D` and `--> Int:U` 1403 | 1404 | それはそれとして、これらの型の制限をつかうことはとても実用的です。 + 1405 | 下記は以前の例の修正版です。返り値の `Int` が定義されているように強制するために `:D` を使っています。 1406 | 1407 | [source,perl6] 1408 | ---- 1409 | sub squared ($x --> Int:D) { 1410 | return $x ** 2; 1411 | } 1412 | say "1.2 squared is equal to " ~ squared(1.2); 1413 | ---- 1414 | ==== 1415 | 1416 | NOTE: もしサブルーチンと関数に関する情報をもっと知りたいのであれば、次のURLを参照することをすすめます https://docs.raku.org/language/functions 1417 | 1418 | == 関数型プログラミング 1419 | この章では関数型プログラミングを容易にしてくれるいくつかの機能を見ていこうと思います。 1420 | 1421 | === 関数は第一級オブジェクト 1422 | 関数/サブルーチンは第一級オブジェクトです: 1423 | 1424 | * 引数として用いることができます 1425 | 1426 | * 別の関数から返すことができます 1427 | 1428 | * 変数に代入することができます 1429 | 1430 | この概念を説明するためのよい例は `map` 関数です。 + 1431 | `map` は _高階関数_ です。ほかの関数を引数として受け取ることができます。 1432 | 1433 | [source,perl6] 1434 | .スクリプト 1435 | ---- 1436 | my @array = <1 2 3 4 5>; 1437 | sub squared($x) { 1438 | $x ** 2 1439 | } 1440 | say map(&squared,@array); 1441 | ---- 1442 | 1443 | .出力 1444 | ---- 1445 | (1 4 9 16 25) 1446 | ---- 1447 | 1448 | .説明 1449 | まず `squared` と呼ばれるサブルーチンを定義しました。このサブルーチンは引数として与えられた値を二乗します。 1450 | 次に、高階関数である `map` に対して、このサブルーチンと配列の二つの引数を与えます。 1451 | 結果は、配列の各要素の平方のリストとなります。 1452 | 1453 | 引数としてサブルーチンを用いるときはその名前の前に `&` をつけなければならないことに注意してください。 1454 | 1455 | === 無名関数 1456 | *無名関数* は *ラムダ* とも呼ばれています。 + 1457 | 1458 | 無名関数は識別子に束縛されません。(名前をもっていないため) 1459 | 1460 | `map` の例を書き換えて無名関数を使うようにしましょう 1461 | [source,perl6] 1462 | ---- 1463 | my @array = <1 2 3 4 5>; 1464 | say map(-> $x {$x ** 2},@array); 1465 | ---- 1466 | サブルーチンを宣言して `map` の引数として渡す代わりに、 `map` の中で直接定義していることに注意してください。 + 1467 | 無名関数の中で `\-> $x {$x ** 2}` のように定義しています。 1468 | 1469 | Rakuではこのような使われ方の無名関数を *ポインティブロック* と呼びます。 1470 | 1471 | [source,perl6] 1472 | .ポインティブロックは関数を変数に代入するときにも使われます: 1473 | ---- 1474 | my $squared = -> $x { 1475 | $x ** 2 1476 | } 1477 | say $squared(9); 1478 | ---- 1479 | 1480 | === チェイン 1481 | Rakuでは、メソッドはチェインすることができます。メソッドの結果を引数としてほかのメソッドに渡す必要はありません。 1482 | 1483 | 例題: 配列が与えられているとして、値の重複が無いように降順にソートしてください。 + 1484 | これはチェインを使わなかった場合の解答です: 1485 | [source,perl6] 1486 | ---- 1487 | my @array = <7 8 9 0 1 2 4 3 5 6 7 8 9>; 1488 | my @final-array = reverse(sort(unique(@array))); 1489 | say @final-array; 1490 | ---- 1491 | この例では `@array` に対して `unique` 関数を呼び、その結果を `sort` の引数として渡します。さらにそのソートした結果を `reverse` に渡します。 1492 | 1493 | 対照的に、上記の例は *メソッドチェイン* を利用して、次のように書くことができます。: 1494 | 1495 | [source,perl6] 1496 | ---- 1497 | my @array = <7 8 9 0 1 2 4 3 5 6 7 8 9>; 1498 | my @final-array = @array.unique.sort.reverse; 1499 | say @final-array; 1500 | ---- 1501 | 1502 | メソッドチェインは _見た目が良い_ ということが一目瞭然ですね。 1503 | 1504 | === フィード演算子 1505 | *フィード演算子* は、関数型言語では _パイプ_ と呼ばれ、メソッドチェインをさらに分かりやすくしてくれます。 1506 | [source,perl6] 1507 | .前方フィード 1508 | ---- 1509 | my @array = <7 8 9 0 1 2 4 3 5 6 7 8 9>; 1510 | @array ==> unique() 1511 | ==> sort() 1512 | ==> reverse() 1513 | ==> my @final-array; 1514 | say @final-array; 1515 | ---- 1516 | 1517 | .説明 1518 | ---- 1519 | まず `@array`で始まり 次に重複のないリストを返します 1520 | 次にソートします 1521 | 次にリバースします 1522 | 次に結果を `@final-array` に格納します 1523 | ---- 1524 | メソッドの呼び出し順は、最初から最後のステップへのトップダウンであることに注目してください。 1525 | 1526 | 1527 | [source,perl6] 1528 | .後方フィード 1529 | ---- 1530 | my @array = <7 8 9 0 1 2 4 3 5 6 7 8 9>; 1531 | my @final-array-v2 <== reverse() 1532 | <== sort() 1533 | <== unique() 1534 | <== @array; 1535 | say @final-array-v2; 1536 | ---- 1537 | 1538 | .説明 1539 | 後方フィードは前方フィードに似ていますが、順序が逆です。 + 1540 | メソッドの呼び出し順は、最後から最初のステップへのボトムアップであることに注目してください。 1541 | 1542 | === ハイパー演算子 1543 | *ハイパー演算子* `>>.` はリストの要素のすべてに対してメソッドの呼び出しを行い、その結果のリストを返します。 1544 | 1545 | [source,perl6] 1546 | ---- 1547 | my @array = <0 1 2 3 4 5 6 7 8 9 10>; 1548 | sub is-even($var) { $var %% 2 }; 1549 | 1550 | say @array>>.is-prime; 1551 | say @array>>.&is-even; 1552 | ---- 1553 | 1554 | ハイパー演算子を用いることでRakuに組み込まれているメソッドを呼び出すこともできます。例えば、`is-prime` は数値が素数かそうでないかを判別する組み込みのメソッドです。 1555 | 加えて、新しいサブルーチンを定義してハイパーオペレーターを使って呼び出すこともできます。この場合、 `&` をメソッドの先頭に追加しなければなりません。 例えば、`&is-even` といった具合です。 1556 | 1557 | 配列に対して反復処理を行うための `for` によるループを書くことから脱却することができ、とても実用的です。 1558 | 1559 | WARNING: Rakuは元の値の並びと結果の値の並びが同じになることを保証します。 + 1560 | ただし、 Rakuがもとの並びと同じ順番や同じスレッドで実際にメソッドを呼び出しているという *保証はない* です。 1561 | そのため、副作用を持つメソッド、例えば `say` や `print` には注意してください。 1562 | 1563 | === ジャンクション 1564 | *ジャンクション* は値の論理的な重ね合わせです。 1565 | 1566 | 下記の例では `1|2|3` がジャンクションです。 1567 | [source,perl6] 1568 | ---- 1569 | my $var = 2; 1570 | if $var == 1|2|3 { 1571 | say "The variable is 1 or 2 or 3" 1572 | } 1573 | ---- 1574 | ジャンクションの使用は通常は *オートスレッディング* のトリガーとなります。; 1575 | この演算はジャンクションの要素それぞれに対して実行され、すべての結果を結合した新たなジャンクションが生成され、それが返されます。 1576 | 1577 | === 遅延リスト 1578 | *遅延リスト* は遅延評価されるリストです。 + 1579 | 遅延評価とは、必要な時まで式の評価を遅らせ、ルックアップテーブルに結果を保存しておくことで不要な繰り返しの評価を避けるものです。 1580 | 1581 | 以下のような恩恵を含んでいます: 1582 | 1583 | * 不要な計算を避けることでパフォーマンスが向上する 1584 | 1585 | * 潜在的には無限のデータ構造をつくることができる 1586 | 1587 | * 制御フローを定義することができる 1588 | 1589 | 遅延リストをつくるためには接中辞演算子 `...` を用います。 + 1590 | 遅延リストは、*初期要素(複数可)* 、*ジェネレータ* 、*終点* を持っています。 1591 | 1592 | [source,perl6] 1593 | .シンプルな遅延リスト 1594 | ---- 1595 | my $lazylist = (1 ... 10); 1596 | say $lazylist; 1597 | ---- 1598 | 初期要素は1、終点は10です。ジェネレータは定義されていないので、デフォルトのジェネレータは次の値(+1)です + 1599 | つまり、この遅延リストは(もし要求されれば)、(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)という要素のリストを返すでしょう。 1600 | 1601 | [source,perl6] 1602 | .無限遅延リスト 1603 | ---- 1604 | my $lazylist = (1 ... Inf); 1605 | say $lazylist; 1606 | ---- 1607 | この遅延リストは(もし要求されれば)1から無限までの間のすべての整数、つまり任意の整数を返します。 1608 | 1609 | [source,perl6] 1610 | .演繹的に作られたジェネレータによる遅延リスト 1611 | ---- 1612 | my $lazylist = (0,2 ... 10); 1613 | say $lazylist; 1614 | ---- 1615 | 初期要素は0と2で終点は10です。 1616 | ジェネレータは定義されていませんが、初期要素を使ってRakuはジェネレータは(+2)であると演繹します。 + 1617 | この遅延リストは(もし要求されれば)次のような要素を返します。(0, 2, 4, 6, 8, 10) 1618 | 1619 | [source,perl6] 1620 | .定義されたジェネレータによる遅延リスト 1621 | ---- 1622 | my $lazylist = (0, { $_ + 3 } ... 12); 1623 | say $lazylist; 1624 | ---- 1625 | この例では、明示的に `{ }` で囲まれたジェネレータを定義しています。 + 1626 | この遅延リストは(もし要求されれば)次のような要素を返します。(0, 3, 6, 9, 12) 1627 | 1628 | [WARNING] 1629 | ==== 1630 | 明示的なジェネレータを使うときは、終点はジェネレータが返すことのできるような値のひとつでなければなりません。 + 1631 | もし、終点が10になっている上の例で、かわりに終点を12にしたら処理が止まらなくなります。 1632 | ジェネレータは終点を _ジャンプして超える_ のです。 1633 | 1634 | 1635 | 別の選択肢として、`0 ... 10` を `0 ...^ * > 10` に置き換えることもできます + 1636 | このように読みます: 0から10を超えるような最初の値まで(10は除く) 1637 | 1638 | [source,perl6] 1639 | .これではジェネレータは止まりません 1640 | ---- 1641 | my $lazylist = (0, { $_ + 3 } ... 10); 1642 | say $lazylist; 1643 | ---- 1644 | 1645 | [source,perl6] 1646 | .これならジェネレータは止まります 1647 | ---- 1648 | my $lazylist = (0, { $_ + 3 } ...^ * > 10); 1649 | say $lazylist; 1650 | ---- 1651 | ==== 1652 | 1653 | === クロージャ 1654 | RakuのすべてのCode型のオブジェクトはクロージャです。これは外のスコープのレキシカル変数を参照できるということを意味しています。 1655 | 1656 | [source,perl6] 1657 | ---- 1658 | sub generate-greeting { 1659 | my $name = "John Doe"; 1660 | sub greeting { 1661 | say "Good Morning $name"; 1662 | }; 1663 | return &greeting; 1664 | } 1665 | my $generated = generate-greeting; 1666 | $generated(); 1667 | ---- 1668 | 1669 | もし上記のコードを動かしたのなら、 `Good Morning John Doe` とターミナルに表示されたはずです。 1670 | 非常に単純な結果でしたが、この例で面白いところは、中の `greeting` というサブルーチンが、それが実行される前に外のサブルーチンから返されているということです。 1671 | 1672 | `$generated` は *クロージャ* になったのです。 1673 | 1674 | *クロージャ* は二つのものを結びつけた特別な種類のオブジェクトです: 1675 | 1676 | * サブルーチン 1677 | 1678 | * サブルーチンの生成された環境 1679 | 1680 | 環境は、そのクロージャが生成されたときにスコープ内にあったすべてのローカル変数で構成されています。 1681 | この場合、 `$generated` はサブルーチン `greeting` と文字列 `John Doe` の両方を結びつけたクロージャといえます。 1682 | 1683 | では、もっと面白い例を見てみましょう。 1684 | [source,perl6] 1685 | ---- 1686 | sub greeting-generator($period) { 1687 | return sub ($name) { 1688 | return "Good $period $name" 1689 | } 1690 | } 1691 | my $morning = greeting-generator("Morning"); 1692 | my $evening = greeting-generator("Evening"); 1693 | 1694 | say $morning("John"); 1695 | say $evening("Jane"); 1696 | ---- 1697 | この例では、`$period` というひとつの引数を受け取って新しいサブルーチンを返すような、サブルーチン `greeting-generator($period)` を定義しました。このサブルーチンが返す新しいサブルーチンは `$name` という引数を受け取り、そして作成されたあいさつを返します。 1698 | 1699 | 基本的に、`greeting-generator` はサブルーチンのファクトリです。この例では、我々は `greeting-generator` を二つの新しいサブルーチンを生成するために用いました。 1700 | ひとつは `Good Morning` とあいさつし、もうひとつは `Good Evening` とあいさつします。 1701 | 1702 | `$morning` と `$evening` は両方ともクロージャです。同じサブルーチン本体の定義を共有していますが、違う環境を保存しています。 + 1703 | `$morning` の環境では `$period` は `Morning` です。 1704 | `$evening` の環境では `$period` は `Evening` です。 1705 | 1706 | == クラスとオブジェクト 1707 | 前章では、どうやってRakuが関数型プログラミングを楽にしてくれるかについて学びました。 + 1708 | この章ではRakuにおけるオブジェクト指向プログラミングについてみていきましょう。 1709 | 1710 | === イントロダクション 1711 | 1712 | _オブジェクト指向_ プログラミングは昨今広く使われているパラダイムの一つです。 + 1713 | *オブジェクト* は一緒にバンドルされた変数やサブルーチンの集合です。 + 1714 | 変数は *属性* と呼ばれ、サブルーチンは *メソッド* とよばれます。 + 1715 | 属性はオブジェクトの *状態* を定義し、メソッドはオブジェクトの *ふるまい* を定義します。 1716 | 1717 | *クラス* は *オブジェクト* を作るための鋳型です。 + 1718 | 1719 | これらの関係を理解するために、下記の例を考えてみてください: 1720 | 1721 | |=== 1722 | 1723 | | 現在四人が部屋にいる | *オブジェクト* => 4 人 1724 | 1725 | | 四人は人間である | *クラス* => 人間 1726 | 1727 | | 四人はそれぞれ異なった名前、年齢、性別、国籍を持っている | *属性* => 名前、年齢、性別、国籍 1728 | 1729 | |=== 1730 | 1731 | _オブジェクト指向_ の用語では、これらのオブジェクトはクラスの *インスタンス* と呼ばれています。 1732 | 1733 | 下記のスクリプトについて考えてみてください: 1734 | [source,perl6] 1735 | ---- 1736 | class Human { 1737 | has $.name; 1738 | has $.age; 1739 | has $.sex; 1740 | has $.nationality; 1741 | } 1742 | 1743 | my $john = Human.new(name => 'John', age => 23, sex => 'M', nationality => 'American'); 1744 | say $john; 1745 | ---- 1746 | `class` キーワードはクラスを定義するのに使われます。 + 1747 | `has` キーワードはクラスの属性を定義するのに使われます。 + 1748 | `.new()` メソッドは *コンストラクタ* と呼ばれるものです。そのメソッドの呼ばれたクラスのインスタンスとしてオブジェクトを生成します。 1749 | 1750 | 上記のスクリプトでは、新しい変数 `$john` は、 `Human.new()` によって定義された"Human"の新しいインスタンスを持っています。 1751 | クラスは `my` を使うことで _レキシカルスコープ_ とすることもできます: 1752 | [source,perl6] 1753 | ---- 1754 | my class Human { 1755 | 1756 | } 1757 | ---- 1758 | 1759 | === カプセル化 1760 | カプセル化は、データとメソッドの集合を一緒にバンドルするというオブジェクト指向の概念です。 + 1761 | オブジェクト内のデータ(属性)は *プライベート* であるべきです。つまり、オブジェクトの中からしかアクセスできないようにするべきです。 + 1762 | オブジェクトの外から属性にアクセスするためには *アクセッサ* と呼ばれるメソッドを用います。 1763 | 1764 | 下記の二つのスクリプトは同じ結果を返します。 1765 | 1766 | .変数への直接のアクセス: 1767 | [source,perl6] 1768 | ---- 1769 | my $var = 7; 1770 | say $var; 1771 | ---- 1772 | 1773 | .カプセル化: 1774 | [source,perl6] 1775 | ---- 1776 | my $var = 7; 1777 | sub sayvar { 1778 | $var; 1779 | } 1780 | say sayvar; 1781 | ---- 1782 | `sayvar` メソッドはアクセッサです。変数に直接アクセスしなくても、変数の値にアクセスできるようにしてくれます。 1783 | 1784 | Rakuでは *トゥイジル* の使用によって楽にカプセル化を行うことができます。 + 1785 | トゥイジルは補助的な _シジル_ です。シジルと属性の名前の間に書きます。 + 1786 | 二つのトゥイジルがクラスでは使われます: 1787 | 1788 | * `!` は明示的に属性がプライベートであることを宣言するときに使います 1789 | * `.` は属性のアクセッサを自動的に生成するときに使います 1790 | 1791 | デフォルトでは、すべての属性はプライベートですが、いつも `!` トゥイジルを使うことはよい習慣です。 1792 | 1793 | したがって、上のクラスは次のように書き換えるべきです: 1794 | [source,perl6] 1795 | ---- 1796 | class Human { 1797 | has $!name; 1798 | has $!age; 1799 | has $!sex; 1800 | has $!nationality; 1801 | } 1802 | 1803 | my $john = Human.new(name => 'John', age => 23, sex => 'M', nationality => 'American'); 1804 | say $john; 1805 | ---- 1806 | 次の命令をスクリプトに追加してみましょう: `say $john.age;` + 1807 | このようなエラーが返ってくるはずです: `Method 'age' not found for invocant of class 'Human'` + 1808 | `$!age` はプライベートでありオブジェクト内でしか使えないというのが原因です。 1809 | オブジェクトの外からアクセスしようとするとエラーが返ります。 1810 | 1811 | では、`has $!age` を `has $.age` に置き換えて、`say $john.age;` の結果を見てみましょう。 1812 | 1813 | === 名前付き引数と固定パラメータ 1814 | Rakuでは、すべてのクラスはデフォルトの `.new()` コンストラクタを継承しています。 + 1815 | このコンストラクタは引数を与えてオブジェクトを生成することもできます。 + 1816 | デフォルトのコンストラクタは *名前付き引数* のみ使用することができます。 + 1817 | 上記の例において、 `.new()` に与えられている引数がすべて名前付きであることに注目してください。 1818 | 1819 | * name \=> 'John' 1820 | 1821 | * age \=> 23 1822 | 1823 | では、もしオブジェクトを生成するときにいちいち属性の名前を指定したくなかったらどうしたらいいでしょう? + 1824 | そういう場合は、 *固定引数* を受け取るような別のコンストラクタを作る必要があります。 1825 | 1826 | [source,perl6] 1827 | ---- 1828 | class Human { 1829 | has $.name; 1830 | has $.age; 1831 | has $.sex; 1832 | has $.nationality; 1833 | # デフォルトのコンストラクタをオーバーライドする 1834 | method new ($name,$age,$sex,$nationality) { 1835 | self.bless(:$name,:$age,:$sex,:$nationality); 1836 | } 1837 | } 1838 | 1839 | my $john = Human.new('John',23,'M','American'); 1840 | say $john; 1841 | ---- 1842 | 1843 | === メソッド 1844 | 1845 | ==== イントロダクション 1846 | メソッドはオブジェクトの _サブルーチン_ です。 + 1847 | サブルーチンのように、機能の集合をパッケージングするための手段であり、 *引数* を受け取り、 *シグネチャ* を持ち、*複数* あるとして定義することができます。 1848 | 1849 | メソッドは `method` キーワードを用いることで定義されます。 1850 | 一般的な状況では、メソッドはオブジェクトの属性に対して何かしらの処理を行うことを要求されます。 1851 | これはカプセル化の考え方を強化します。オブジェクトの属性はメソッドを通じてオブジェクトの中からしか操作できません。 1852 | 外の世界からはオブジェクトのメソッドとしかやりとりすることができず、そのオブジェクトの属性に直接アクセスすることはできません。 1853 | 1854 | [source,perl6] 1855 | ---- 1856 | class Human { 1857 | has $.name; 1858 | has $.age; 1859 | has $.sex; 1860 | has $.nationality; 1861 | has $.eligible; 1862 | method assess-eligibility { 1863 | if self.age < 21 { 1864 | $!eligible = 'No' 1865 | } else { 1866 | $!eligible = 'Yes' 1867 | } 1868 | } 1869 | 1870 | } 1871 | 1872 | my $john = Human.new(name => 'John', age => 23, sex => 'M', nationality => 'American'); 1873 | $john.assess-eligibility; 1874 | say $john.eligible; 1875 | ---- 1876 | 1877 | 一度クラスの中でメソッドが定義されたら、 _ドット表記法_ によってオブジェクトから呼び出すことができます。: + 1878 | _オブジェクト_ *.* _メソッド_ として、上記の例のように: `$john.assess-eligibility` 1879 | 1880 | メソッドの定義の中では、他のメソッドを呼び出すためにオブジェクトそれ自身への参照が必要な場合は `self` キーワードを使います。 + 1881 | 1882 | メソッドの定義の中では、属性を参照する必要がある場合は、その属性が `.` をともなって定義されていても `!` を使います。 + 1883 | そういったことを行う論理的根拠は、 `.` トゥイジルが行っていることは `!` をともなった属性を宣言し、アクセッサを自動で生成することであるということです。 1884 | 1885 | 上の例では `if self.age < 21` と `if $!age < 21` は同じ効果を持っているかもしれませんが、理屈の上では違うということになっています: 1886 | 1887 | * `self.age` は `.age` メソッド (アクセッサ) を呼びます + 1888 | `$.age` とも書けます 1889 | * `$!age` は変数への直接の呼び出しです 1890 | 1891 | ==== プライベートメソッド 1892 | 通常のメソッドはクラスの外でもオブジェクトから呼び出すことができます。 1893 | 1894 | *プライベートメソッド* はクラスの中からしか呼び出せないメソッドです。 + 1895 | ユースケースとしては、あるメソッドが、特定の機能を使うために、ほかのメソッドを呼び出すようなときでしょう。 + 1896 | 外の世界とインタフェースで接続されているメソッドはパブリックですが、そこから参照されているメソッドはプライベートなままであるべきです。 + 1897 | 直接ユーザーに呼び出してほしくないのです。そのため、プライベートとして宣言することになります。 1898 | 1899 | プライベートメソッドの宣言では名前の前で `!` トゥイジルを使う必要があります。 + 1900 | プライベートメソッドは `.` の代わりに `!` によって呼び出します。 1901 | 1902 | [source,perl6] 1903 | ---- 1904 | method !iamprivate { 1905 | # コードはここに 1906 | } 1907 | 1908 | method iampublic { 1909 | self!iamprivate; 1910 | # さらに処理を行う 1911 | } 1912 | ---- 1913 | 1914 | === クラス属性 1915 | 1916 | *クラス属性* はクラス自身に属しているがオブジェクトには属していないような属性です。 + 1917 | 定義のときに初期化することができます。 + 1918 | クラス属性は `has` の代わりに `my` をつかうことで宣言します。 1919 | クラス属性はそのオブジェクトではなくクラスそれ自身を呼び出します。 1920 | 1921 | [source,perl6] 1922 | ---- 1923 | class Human { 1924 | has $.name; 1925 | my $.counter = 0; 1926 | method new($name) { 1927 | Human.counter++; 1928 | self.bless(:$name); 1929 | } 1930 | } 1931 | my $a = Human.new('a'); 1932 | my $b = Human.new('b'); 1933 | 1934 | say Human.counter; 1935 | ---- 1936 | 1937 | === アクセスタイプ 1938 | 今までの見てきた例では、アクセッサはオブジェクトの属性から情報を *得る* ために使われてきました。 1939 | 1940 | 属性の値を変更する必要があるとしたらどうでしょう? + 1941 | `is rw` キーワードを使って、_読み/書き_ ラベルを付与する必要があります。 1942 | [source,perl6] 1943 | ---- 1944 | class Human { 1945 | has $.name; 1946 | has $.age is rw; 1947 | } 1948 | my $john = Human.new(name => 'John', age => 21); 1949 | say $john.age; 1950 | 1951 | $john.age = 23; 1952 | say $john.age; 1953 | ---- 1954 | デフォルトでは、すべての属性は _読み込み専用_ として宣言されます。しかし明示的に `is readonly` を使うこともできます。 1955 | 1956 | === 継承 1957 | ==== イントロダクション 1958 | *継承は* オブジェクト指向プログラミングのもう一つの考え方です。 1959 | 1960 | クラスを定義すると、たくさんのクラスで同じ属性/メソッドを使っているということにすぐ気づくでしょう。 + 1961 | コードは重複しているべきでしょうか? 1962 | ダメです! *継承* を使うべきです。 1963 | 1964 | 人間クラスと従業員クラスの二つのクラスを定義したいという状況を考えてみましょう。 + 1965 | 人間は二つの属性を持っています: 名前と年齢 + 1966 | 従業員は四つの属性を持っています: 名前、年齢、会社、給料 1967 | 1968 | 次のようにクラスを定義しようとするかもしれません: 1969 | [source,perl6] 1970 | ---- 1971 | class Human { 1972 | has $.name; 1973 | has $.age; 1974 | } 1975 | 1976 | class Employee { 1977 | has $.name; 1978 | has $.age; 1979 | has $.company; 1980 | has $.salary; 1981 | } 1982 | ---- 1983 | 理屈の上では正しいですが、上記のコードの考え方はお粗末です。 1984 | 1985 | 次のような書き方のほうが良いでしょう: 1986 | [source,perl6] 1987 | ---- 1988 | class Human { 1989 | has $.name; 1990 | has $.age; 1991 | } 1992 | 1993 | class Employee is Human { 1994 | has $.company; 1995 | has $.salary; 1996 | } 1997 | ---- 1998 | `is` キーワードは継承を宣言しています。 + 1999 | オブジェクト指向の用語では従業員は人間の *子* であり、人間は従業員の *親* であるといいます。 2000 | 2001 | すべての子クラスは親クラスの属性とメソッドを継承します。そのため再定義する必要はありません。 2002 | 2003 | ==== オーバーライド 2004 | クラスは親クラスからすべての属性とメソッドを継承します。 + 2005 | 継承したメソッドとは違うふるまいを子クラスのそれが行う必要がある場合があります。 + 2006 | こういった場合は子クラスにおいてメソッドを再定義します。 + 2007 | この考え方は *オーバーライド* と呼ばれます。 2008 | 2009 | 下記の例では、`introduce-yourself` メソッドが従業員クラスによって継承されています。 2010 | 2011 | [source,perl6] 2012 | ---- 2013 | class Human { 2014 | has $.name; 2015 | has $.age; 2016 | method introduce-yourself { 2017 | say 'Hi i am a human being, my name is ' ~ self.name; 2018 | } 2019 | } 2020 | 2021 | class Employee is Human { 2022 | has $.company; 2023 | has $.salary; 2024 | } 2025 | 2026 | my $john = Human.new(name =>'John', age => 23,); 2027 | my $jane = Employee.new(name =>'Jane', age => 25, company => 'Acme', salary => 4000); 2028 | 2029 | $john.introduce-yourself; 2030 | $jane.introduce-yourself; 2031 | ---- 2032 | オーバーライドは次のように実行されます: 2033 | 2034 | [source,perl6] 2035 | ---- 2036 | class Human { 2037 | has $.name; 2038 | has $.age; 2039 | method introduce-yourself { 2040 | say 'Hi i am a human being, my name is ' ~ self.name; 2041 | } 2042 | } 2043 | 2044 | class Employee is Human { 2045 | has $.company; 2046 | has $.salary; 2047 | method introduce-yourself { 2048 | say 'Hi i am a employee, my name is ' ~ self.name ~ ' and I work at: ' ~ self.company; 2049 | } 2050 | 2051 | } 2052 | 2053 | my $john = Human.new(name =>'John',age => 23,); 2054 | my $jane = Employee.new(name =>'Jane',age => 25,company => 'Acme',salary => 4000); 2055 | 2056 | $john.introduce-yourself; 2057 | $jane.introduce-yourself; 2058 | ---- 2059 | 2060 | オブジェクトがどのクラスのものであるかに依存して、正しいメソッドが呼ばれます。 2061 | 2062 | ==== サブメソッド 2063 | *サブメソッド* は子クラスによって継承されないメソッドです。 + 2064 | 宣言されたクラスからのみアクセスすることができます。 + 2065 | `submethod` キーワードを使って定義されます。 2066 | 2067 | === 多重継承 2068 | Rakuでは多重継承を行うことができます。あるクラスは複数の他のクラスから継承を行うことができます。 2069 | 2070 | [source,perl6] 2071 | ---- 2072 | class bar-chart { 2073 | has Int @.bar-values; 2074 | method plot { 2075 | say @.bar-values; 2076 | } 2077 | } 2078 | 2079 | class line-chart { 2080 | has Int @.line-values; 2081 | method plot { 2082 | say @.line-values; 2083 | } 2084 | } 2085 | 2086 | class combo-chart is bar-chart is line-chart { 2087 | } 2088 | 2089 | my $actual-sales = bar-chart.new(bar-values => [10,9,11,8,7,10]); 2090 | my $forecast-sales = line-chart.new(line-values => [9,8,10,7,6,9]); 2091 | 2092 | my $actual-vs-forecast = combo-chart.new(bar-values => [10,9,11,8,7,10], 2093 | line-values => [9,8,10,7,6,9]); 2094 | say "Actual sales:"; 2095 | $actual-sales.plot; 2096 | say "Forecast sales:"; 2097 | $forecast-sales.plot; 2098 | say "Actual vs Forecast:"; 2099 | $actual-vs-forecast.plot; 2100 | ---- 2101 | 2102 | .`出力` 2103 | ---- 2104 | Actual sales: 2105 | [10 9 11 8 7 10] 2106 | Forecast sales: 2107 | [9 8 10 7 6 9] 2108 | Actual vs Forecast: 2109 | [10 9 11 8 7 10] 2110 | ---- 2111 | 2112 | .説明 2113 | `combo-chart` クラスは二つの系列を保持できるようになっているべきです。一つは実際の値でバーにプロットされます。 2114 | もう一つは予測値で線にプロットされます。 + 2115 | これが `combo-chart` クラスを `line-chart` クラスと `bar-chart` クラスの子として定義した理由です。 + 2116 | `combo-chart` の `plot` メソッドが要求された結果を生成しなかったことに気づいたと思います。 2117 | 系列一つだけがプロットされました。 + 2118 | なぜこんなことが起こったのでしょうか? + 2119 | `combo-chart` は `line-chart` と `bar-chart` を継承しており、両方とも `plot` と呼ばれるメソッドを持っています。 2120 | `combo-chart` からこのメソッドが呼ばれるとき、Rakuの内部では継承されたメソッドのうちの一つだけを呼ぶことでコンフリクトを解消しているのです。 2121 | 2122 | .修正 2123 | 正しくふるまうようにするには、 `combo-chart` の中の `plot` メソッドをオーバーライドするべきでした。 2124 | [source,perl6] 2125 | ---- 2126 | class bar-chart { 2127 | has Int @.bar-values; 2128 | method plot { 2129 | say @.bar-values; 2130 | } 2131 | } 2132 | 2133 | class line-chart { 2134 | has Int @.line-values; 2135 | method plot { 2136 | say @.line-values; 2137 | } 2138 | } 2139 | 2140 | class combo-chart is bar-chart is line-chart { 2141 | method plot { 2142 | say @.bar-values; 2143 | say @.line-values; 2144 | } 2145 | } 2146 | 2147 | my $actual-sales = bar-chart.new(bar-values => [10,9,11,8,7,10]); 2148 | my $forecast-sales = line-chart.new(line-values => [9,8,10,7,6,9]); 2149 | 2150 | my $actual-vs-forecast = combo-chart.new(bar-values => [10,9,11,8,7,10], 2151 | line-values => [9,8,10,7,6,9]); 2152 | say "Actual sales:"; 2153 | $actual-sales.plot; 2154 | say "Forecast sales:"; 2155 | $forecast-sales.plot; 2156 | say "Actual vs Forecast:"; 2157 | $actual-vs-forecast.plot; 2158 | ---- 2159 | 2160 | .`出力` 2161 | ---- 2162 | Actual sales: 2163 | [10 9 11 8 7 10] 2164 | Forecast sales: 2165 | [9 8 10 7 6 9] 2166 | Actual vs Forecast: 2167 | [10 9 11 8 7 10] 2168 | [9 8 10 7 6 9] 2169 | ---- 2170 | 2171 | === ロール 2172 | *ロール* はクラスが属性とメソッドのコレクションであるという点においてクラスと似ています。 2173 | 2174 | ロールは `role` キーワードによって宣言されます。ロールを実装したいクラスは `does` キーワードを使って宣言します。 2175 | 2176 | .ロールを使って多重継承を書き換えてみましょう: 2177 | [source,perl6] 2178 | ---- 2179 | role bar-chart { 2180 | has Int @.bar-values; 2181 | method plot { 2182 | say @.bar-values; 2183 | } 2184 | } 2185 | 2186 | role line-chart { 2187 | has Int @.line-values; 2188 | method plot { 2189 | say @.line-values; 2190 | } 2191 | } 2192 | 2193 | class combo-chart does bar-chart does line-chart { 2194 | method plot { 2195 | say @.bar-values; 2196 | say @.line-values; 2197 | } 2198 | } 2199 | 2200 | my $actual-sales = bar-chart.new(bar-values => [10,9,11,8,7,10]); 2201 | my $forecast-sales = line-chart.new(line-values => [9,8,10,7,6,9]); 2202 | 2203 | my $actual-vs-forecast = combo-chart.new(bar-values => [10,9,11,8,7,10], 2204 | line-values => [9,8,10,7,6,9]); 2205 | say "Actual sales:"; 2206 | $actual-sales.plot; 2207 | say "Forecast sales:"; 2208 | $forecast-sales.plot; 2209 | say "Actual vs Forecast:"; 2210 | $actual-vs-forecast.plot; 2211 | ---- 2212 | 2213 | 上記のスクリプトを実行すると全く同じ結果が出力されることを確認できるはずです。 2214 | 2215 | そろそろこんなひとり言が聞こえてきそうです: もしロールがクラスのようにふるまうなら、ロールの使い道って何だろう? + 2216 | この質問に答えるために、多重継承の例を見せるために使われた最初のスクリプトを修正してください。 2217 | `plot` メソッドをオーバーライドするのを _忘れた_ 例のスクリプトです。 2218 | 2219 | [source,perl6] 2220 | ---- 2221 | role bar-chart { 2222 | has Int @.bar-values; 2223 | method plot { 2224 | say @.bar-values; 2225 | } 2226 | } 2227 | 2228 | role line-chart { 2229 | has Int @.line-values; 2230 | method plot { 2231 | say @.line-values; 2232 | } 2233 | } 2234 | 2235 | class combo-chart does bar-chart does line-chart { 2236 | } 2237 | 2238 | my $actual-sales = bar-chart.new(bar-values => [10,9,11,8,7,10]); 2239 | my $forecast-sales = line-chart.new(line-values => [9,8,10,7,6,9]); 2240 | 2241 | my $actual-vs-forecast = combo-chart.new(bar-values => [10,9,11,8,7,10], 2242 | line-values => [9,8,10,7,6,9]); 2243 | say "Actual sales:"; 2244 | $actual-sales.plot; 2245 | say "Forecast sales:"; 2246 | $forecast-sales.plot; 2247 | say "Actual vs Forecast:"; 2248 | $actual-vs-forecast.plot; 2249 | ---- 2250 | 2251 | .`出力` 2252 | ---- 2253 | ===SORRY!=== 2254 | Method 'plot' must be resolved by class combo-chart because it exists in multiple roles (line-chart, bar-chart) 2255 | ---- 2256 | 2257 | .説明 2258 | もし複数のロールが同じクラスに対して適用され、コンフリクトがあるなら、コンパイルタイムのエラーが投げられます。 + 2259 | これは、多重継承よりもずっと安全なアプローチです。なぜなら、多重継承ではコンフリクトはエラーとして考えられておらずランタイムで単純に解決されてしまうからです。 2260 | 2261 | ロールはコンフリクトがあるときに警告してくれるのです。 2262 | 2263 | === イントロスペクション 2264 | *イントロスペクション* は、オブジェクトの型、オブジェクトの属性、オブジェクトのメソッドといったオブジェクトの情報を得るための処理です。 2265 | 2266 | [source,perl6] 2267 | ---- 2268 | class Human { 2269 | has Str $.name; 2270 | has Int $.age; 2271 | method introduce-yourself { 2272 | say 'Hi i am a human being, my name is ' ~ self.name; 2273 | } 2274 | } 2275 | 2276 | class Employee is Human { 2277 | has Str $.company; 2278 | has Int $.salary; 2279 | method introduce-yourself { 2280 | say 'Hi i am a employee, my name is ' ~ self.name ~ ' and I work at: ' ~ self.company; 2281 | } 2282 | } 2283 | 2284 | my $john = Human.new(name =>'John',age => 23,); 2285 | my $jane = Employee.new(name =>'Jane',age => 25,company => 'Acme',salary => 4000); 2286 | 2287 | say $john.WHAT; 2288 | say $jane.WHAT; 2289 | say $john.^attributes; 2290 | say $jane.^attributes; 2291 | say $john.^methods; 2292 | say $jane.^methods; 2293 | say $jane.^parents; 2294 | if $jane ~~ Human {say 'Jane is a Human'}; 2295 | ---- 2296 | イントロスペクションは次のように容易に行えます: 2297 | 2298 | * `.WHAT` -- オブジェクトがどのクラスから作られたかを返します。 2299 | 2300 | * `.^attributes` -- オブジェクトのすべての属性を返します。 2301 | 2302 | * `.^methods` -- オブジェクトから呼ぶことのできるすべてのメソッドを返します。 2303 | 2304 | * `.^parents` -- オブジェクトの属しているクラスの親クラスを返します。 2305 | 2306 | * `~~` はスマートマッチ演算子を呼びます。 2307 | もしオブジェクトが比較している相手のクラスか、その相手のクラスの継承先のいずれかのクラスから生成されているなら _True_ と評価されます。 2308 | 2309 | [NOTE] 2310 | -- 2311 | もしRakuにおけるオブジェクト指向についてより深く知りたいのであれば、次のURLを参照することをすすめます 2312 | 2313 | * https://docs.raku.org/language/classtut 2314 | * https://docs.raku.org/language/objects 2315 | -- 2316 | 2317 | == 例外処理 2318 | 2319 | === 例外のキャッチ 2320 | *例外* はランライムで何かが失敗したときに発生する特別なふるまいです。 + 2321 | 例外が _投げられる_ と表現します。 2322 | 2323 | 正しく実行される下記のスクリプトについて考えてみてください: 2324 | 2325 | [source,perl6] 2326 | ---- 2327 | my Str $name; 2328 | $name = "Joanna"; 2329 | say "Hello " ~ $name; 2330 | say "How are you doing today?" 2331 | ---- 2332 | 2333 | .`出力` 2334 | ---- 2335 | Hello Joanna 2336 | How are you doing today? 2337 | ---- 2338 | 2339 | では、例外を投げる次のスクリプトについて考えてみてください: 2340 | 2341 | [source,perl6] 2342 | ---- 2343 | my Str $name; 2344 | $name = 123; 2345 | say "Hello " ~ $name; 2346 | say "How are you doing today?" 2347 | ---- 2348 | 2349 | .`出力` 2350 | ---- 2351 | Type check failed in assignment to $name; expected Str but got Int 2352 | in block at exceptions.raku:2 2353 | ---- 2354 | 2355 | エラーが発生したとき(この場合は文字列変数に数値を代入している)は必ずプログラムが停止し、ほかの行のコードは評価されないということに注目してください。 2356 | 2357 | *エラー処理* は _投げられた_ 例外の _キャッチ_ 処理を行うことでスクリプトが実行を続けられるようにすることです。 2358 | 2359 | [source,perl6] 2360 | ---- 2361 | my Str $name; 2362 | try { 2363 | $name = 123; 2364 | say "Hello " ~ $name; 2365 | CATCH { 2366 | default { 2367 | say "Can you tell us your name again, we couldn't find it in the register."; 2368 | } 2369 | } 2370 | } 2371 | say "How are you doing today?"; 2372 | ---- 2373 | 2374 | .`出力` 2375 | ---- 2376 | Can you tell us your name again, we couldn't find it in the register. 2377 | How are you doing today? 2378 | ---- 2379 | 2380 | 例外処理は `try-catch` ブロックを用いることで行われます。 2381 | 2382 | [source,perl6] 2383 | ---- 2384 | try { 2385 | # コードはここに 2386 | # もし何かが失敗したなら下記のCATCHブロックに入ります 2387 | # もし問題がなかったのなら下記のCATCHブロックは無視されます 2388 | CATCH { 2389 | default { 2390 | # ここのコードは例外が投げられたときだけ評価されます 2391 | } 2392 | } 2393 | } 2394 | ---- 2395 | 2396 | `CATCH` ブロックは `given` ブロックが定義されるときと同じように定義できます。 2397 | これは様々なタイプの例外を _キャッチ_ して扱うことができることを意味しています。 2398 | 2399 | [source,perl6] 2400 | ---- 2401 | try { 2402 | # コードはここに 2403 | # もし何かが失敗したなら下記のCATCHブロックに入ります 2404 | # もし問題がなかったのなら下記のCATCHブロックは無視されます 2405 | CATCH { 2406 | when X::AdHoc { # X::AdHoc型の例外が投げられたのなら何かを実行します } 2407 | when X::IO { # X::IO型の例外が投げられたのなら何かを実行します } 2408 | when X::OS { # X::OS型の例外が投げられたのなら何かを実行します } 2409 | default { # 上記の型に該当しない例外が投げられたのなら何かを実行します } 2410 | } 2411 | } 2412 | ---- 2413 | 2414 | === 例外を投げる 2415 | Rakuは明示的に例外を投げることができます。 + 2416 | 二つのタイプの例外を投げることができます: 2417 | 2418 | * アドホック例外 2419 | 2420 | * 型付き例外 2421 | 2422 | [source,perl6] 2423 | .アドホック 2424 | ---- 2425 | my Int $age = 21; 2426 | die "Error !"; 2427 | ---- 2428 | 2429 | [source,perl6] 2430 | .型付き 2431 | ---- 2432 | my Int $age = 21; 2433 | X::AdHoc.new(payload => 'Error !').throw; 2434 | ---- 2435 | 2436 | アドホック例外は、例外メッセージのともなった `die` サブルーチンを使って投げられます。 2437 | 2438 | 型付き例外はオブジェクトです。したがって上記の例では `.new()` コンストラクタを使用しています。 + 2439 | すべての型付き例外はクラス `X` の子孫です。下記は少数の例です: 2440 | `X::AdHoc` は最もシンプルな例外のタイプです  + 2441 | `X::IO` はIOエラーに関する例外です + 2442 | `X::OS` はOSエラーに関する例外です + 2443 | `X::Str::Numeric` は文字列を数値にしようとすることに関する例外です 2444 | 2445 | NOTE: もし例外の型と、関連するメソッドのすべてのリストを知りたいのであれば、次のURLを参照することをすすめます https://docs.raku.org/type.html 2446 | 2447 | == 正規表現 2448 | 正規表現、または _regex_ はパターンマッチングのための文字のシーケンスです。 2449 | パターンだと思ってください。 2450 | 2451 | [source,perl6] 2452 | ---- 2453 | if 'enlightenment' ~~ m/ light / { 2454 | say "enlightenment contains the word light"; 2455 | } 2456 | ---- 2457 | 2458 | この例では、スマートマッチ演算子 `~~` は文字列(enlightenment)が単語(light)を含んでいるかどうか調べるのに使われています。 + 2459 | "Enlightenment" は 正規表現 `m/ light /` にマッチします。 2460 | 2461 | === 正規表現の定義 2462 | 2463 | 正規表現は次のように定義できます: 2464 | 2465 | * `/light/` 2466 | 2467 | * `m/light/` 2468 | 2469 | * `rx/light/` 2470 | 2471 | 明示的に指定されない限り、空白は無視されます。つまり、`m/light/` と `m/ light /` は等価です。 2472 | 2473 | === 文字のマッチング 2474 | アルファベット文字とアンダースコア `_` はそのまま書かれます。 + 2475 | 他の文字はバックスラッシュを使うかクォートで囲むことでエスケープされている必要があります。 2476 | 2477 | [source,perl6] 2478 | .バックスラッシュ 2479 | ---- 2480 | if 'Temperature: 13' ~~ m/ \: / { 2481 | say "The string provided contains a colon :"; 2482 | } 2483 | ---- 2484 | 2485 | [source,perl6] 2486 | .シングルクォート 2487 | ---- 2488 | if 'Age = 13' ~~ m/ '=' / { 2489 | say "The string provided contains an equal character = "; 2490 | } 2491 | ---- 2492 | 2493 | [source,perl6] 2494 | .ダブルクォート 2495 | ---- 2496 | if 'name@company.com' ~~ m/ "@" / { 2497 | say "This is a valid email address because it contains an @ character"; 2498 | } 2499 | ---- 2500 | 2501 | === 文字種へのマッチング 2502 | 文字は文字種に分類することができ、これらに対してマッチングを行うことができます。 + 2503 | またその分類と逆の分類(その分類を除いたものすべて)に対してマッチングを行うこともできます。 2504 | 2505 | |=== 2506 | 2507 | | *種類* | *正規表現* | *逆* | *正規表現* 2508 | 2509 | | 単語構成文字 (文字、数字、アンダースコア) | \w | 非単語構成文字 | \W 2510 | 2511 | | 数字 | \d | 非数字 | \D 2512 | 2513 | | 空白文字 | \s | 非空白文字 | \S 2514 | 2515 | | 水平空白文字 | \h | 非水平空白文字 | \H 2516 | 2517 | | 垂直空白文字 | \v | 非垂直空白文字 | \V 2518 | 2519 | | タブ | \t | 非タブ | \T 2520 | 2521 | | 改行 | \n | 非改行 | \N 2522 | 2523 | |=== 2524 | 2525 | [source,perl6] 2526 | ---- 2527 | if "John123" ~~ / \d / { 2528 | say "This is not a valid name, numbers are not allowed"; 2529 | } else { 2530 | say "This is a valid name" 2531 | } 2532 | if "John-Doe" ~~ / \s / { 2533 | say "This string contains whitespace"; 2534 | } else { 2535 | say "This string doesn't contain whitespace" 2536 | } 2537 | ---- 2538 | 2539 | === ユニコードのプロパティ 2540 | 前節での文字種に対するマッチングは便利です。 + 2541 | そうはいっても、もっと系統的なアプローチはユニコードのプロパティを使うことです。 + 2542 | 標準ASCII文字コードの範囲内の文字種に対しても、そうでない文字種に対してもマッチングができるようになります。 + 2543 | ユニコードのプロパティは `<: >` で囲まれます。 2544 | 2545 | [source,perl6] 2546 | ---- 2547 | if "Devangari Numbers १२३" ~~ / <:N> / { 2548 | say "Contains a number"; 2549 | } else { 2550 | say "Doesn't contain a number" 2551 | } 2552 | if "Привет, Иван." ~~ / <:Lu> / { 2553 | say "Contains an uppercase letter"; 2554 | } else { 2555 | say "Doesn't contain an upper case letter" 2556 | } 2557 | if "John-Doe" ~~ / <:Pd> / { 2558 | say "Contains a dash"; 2559 | } else { 2560 | say "Doesn't contain a dash" 2561 | } 2562 | ---- 2563 | 2564 | === ワイルドカード 2565 | 正規表現ではワイルドカードも用いることができます。 2566 | 2567 | ドット `.` は任意の一文字を意味します。 2568 | 2569 | [source,perl6] 2570 | ---- 2571 | if 'abc' ~~ m/ a.c / { 2572 | say "Match"; 2573 | } 2574 | if 'a2c' ~~ m/ a.c / { 2575 | say "Match"; 2576 | } 2577 | if 'ac' ~~ m/ a.c / { 2578 | say "Match"; 2579 | } else { 2580 | say "No Match"; 2581 | } 2582 | ---- 2583 | 2584 | === 量指定子 2585 | 量指定子は文字の後に付けられ、その文字が何回出現するのか指定するために使われます。 2586 | 2587 | クエスチョンマーク `?` は0か1回を意味します。 2588 | 2589 | [source,perl6] 2590 | ---- 2591 | if 'ac' ~~ m/ a?c / { 2592 | say "Match"; 2593 | } else { 2594 | say "No Match"; 2595 | } 2596 | if 'c' ~~ m/ a?c / { 2597 | say "Match"; 2598 | } else { 2599 | say "No Match"; 2600 | } 2601 | ---- 2602 | 2603 | スター `*` は0か複数回を意味します。 2604 | 2605 | [source,perl6] 2606 | ---- 2607 | if 'az' ~~ m/ a*z / { 2608 | say "Match"; 2609 | } else { 2610 | say "No Match"; 2611 | } 2612 | if 'aaz' ~~ m/ a*z / { 2613 | say "Match"; 2614 | } else { 2615 | say "No Match"; 2616 | } 2617 | if 'aaaaaaaaaaz' ~~ m/ a*z / { 2618 | say "Match"; 2619 | } else { 2620 | say "No Match"; 2621 | } 2622 | if 'z' ~~ m/ a*z / { 2623 | say "Match"; 2624 | } else { 2625 | say "No Match"; 2626 | } 2627 | ---- 2628 | 2629 | `+` は少なくとも一回を意味します。 2630 | 2631 | [source,perl6] 2632 | ---- 2633 | if 'az' ~~ m/ a+z / { 2634 | say "Match"; 2635 | } else { 2636 | say "No Match"; 2637 | } 2638 | if 'aaz' ~~ m/ a+z / { 2639 | say "Match"; 2640 | } else { 2641 | say "No Match"; 2642 | } 2643 | if 'aaaaaaaaaaz' ~~ m/ a+z / { 2644 | say "Match"; 2645 | } else { 2646 | say "No Match"; 2647 | } 2648 | if 'z' ~~ m/ a+z / { 2649 | say "Match"; 2650 | } else { 2651 | say "No Match"; 2652 | } 2653 | ---- 2654 | 2655 | === マッチ結果 2656 | 正規表現に対する文字列のマッチング処理が成功したときはいつでも、 2657 | そのマッチ結果は特別な変数 `$/` に格納されます。 2658 | 2659 | [source,perl6] 2660 | .スクリプト 2661 | ---- 2662 | if 'Rakudo is a Perl 6 compiler' ~~ m/:s Perl 6/ { 2663 | say "The match is: " ~ $/; 2664 | say "The string before the match is: " ~ $/.prematch; 2665 | say "The string after the match is: " ~ $/.postmatch; 2666 | say "The matching string starts at position: " ~ $/.from; 2667 | say "The matching string ends at position: " ~ $/.to; 2668 | } 2669 | ---- 2670 | 2671 | .出力 2672 | ---- 2673 | The match is: Perl 6 2674 | The string before the match is: Rakudo is a 2675 | The string after the match is: compiler 2676 | The matching string starts at position: 12 2677 | The matching string ends at position: 18 2678 | ---- 2679 | 2680 | .説明 2681 | `$/` は _マッチオブジェクト_ (正規表現がマッチした文字列) を返します + 2682 | _マッチオブジェクト_ から次のようなメソッドを呼ぶことができます: + 2683 | `.prematch` はマッチの前の文字列を返します。 + 2684 | `.postmatch` はマッチの後ろの文字列を返します。 + 2685 | `.from` はマッチの開始位置を返します。 + 2686 | `.to` はマッチの終了位置を返します。 + 2687 | 2688 | TIP: デフォルトでは正規表現の定義における空白文字は無視されます。 + 2689 | もし、空白文字を含んだ正規表現に対してマッチさせたいのであれば明示的にそうする必要があります。 + 2690 | 正規表現 `m/:s Perl 6/` の中の `:s` は空白文字も考慮するように強制します。 + 2691 | 別の選択肢としては `m/ Perl\s6 /` と書くこともできます。`\s` は空白文字を表します。 + 2692 | もし正規表現が空白文字を一つより多く含んでいるなら、`:s` を使うと `\s` を空白文字が出現する箇所でいちいち書くのと比べると良い選択肢です。 2693 | 2694 | === 例 2695 | emailが正しいかどうか調べましょう。 + 2696 | この例のために正しいemailのアドレスは次のような形式であるとしましょう: 2697 | ファーストネーム [dot] ラストネーム [at] 会社名 [dot] (com/org/net) 2698 | 2699 | WARNING: この例で使われている正規表現はあまり正確ではありません。 + 2700 | Rakuにおける正規表現の機能を説明することが唯一の目的です。 2701 | プロダクションでそのまま使わないでください。 2702 | 2703 | [source,perl6] 2704 | .スクリプト 2705 | ---- 2706 | my $email = 'john.doe@perl6.org'; 2707 | my $regex = / <:L>+\.<:L>+\@<:L+:N>+\.<:L>+ /; 2708 | 2709 | if $email ~~ $regex { 2710 | say $/ ~ " is a valid email"; 2711 | } else { 2712 | say "This is not a valid email"; 2713 | } 2714 | ---- 2715 | 2716 | .出力 2717 | `john.doe@perl6.org is a valid email` 2718 | 2719 | .説明 2720 | `<:L>` は一つの文字にマッチします + 2721 | `<:L>+` は一つ以上の文字にマッチします + 2722 | `\.` は一つの[dot] 文字にマッチします + 2723 | `\@` は一つの[at] 文字にマッチします + 2724 | `<:L+:N>` は一つの文字か数字にマッチします + 2725 | `<:L+:N>+` は一つの文字か数字の繰り返しにマッチします + 2726 | 2727 | この正規表現は次のように分解することができます。: 2728 | 2729 | * *ファーストネーム* `<:L>+` 2730 | 2731 | * *[dot]* `\.` 2732 | 2733 | * *ラストネーム* `<:L>+` 2734 | 2735 | * *[at]* `\@` 2736 | 2737 | * *会社名* `<:L+:N>+` 2738 | 2739 | * *[dot]* `\.` 2740 | 2741 | * *com/org/net* `<:L>+` 2742 | 2743 | [source,perl6] 2744 | .また、ある正規表現は複数の名前付き正規表現に分解することができます 2745 | ---- 2746 | my $email = 'john.doe@perl6.org'; 2747 | my regex many-letters { <:L>+ }; 2748 | my regex dot { \. }; 2749 | my regex at { \@ }; 2750 | my regex many-letters-numbers { <:L+:N>+ }; 2751 | 2752 | if $email ~~ / / { 2753 | say $/ ~ " is a valid email"; 2754 | } else { 2755 | say "This is not a valid email"; 2756 | } 2757 | ---- 2758 | 2759 | 名前付き正規表現は次のような文法で定義されます: `my regex regex-name { regex definition }` + 2760 | 名前付き正規表現は次のような文法で呼び出されます: `` 2761 | 2762 | NOTE: もしもっと正規表現について知りたいのであれば、次のURLを参照するのをすすめます https://docs.raku.org/language/regexes 2763 | 2764 | == Rakuのモジュール 2765 | Rakuは汎用プログラミング言語です。下記を含む多数のタスクに取り組むのに使うことができます。 2766 | テキスト操作、グラフィックス、ウェブ、データベース、ネットワークプロトコルなど。 2767 | 2768 | 再利用性はとても重要な概念です、それによってプログラマは新しいタスクに取り組もうとするたびに車輪の再発明を行う必要がなくなります。 2769 | 2770 | Rakuはでは *モジュール* の作成と再配布ができます。それぞれのモジュールはインストールされれば再利用できる機能のパッケージです。 2771 | 2772 | _Zef_ はRakudo Starに付属しているモジュール管理ツールです。 2773 | 2774 | 特定のモジュールをインストールするには、次のコマンドをターミナルで打ってください: 2775 | 2776 | `zef install "モジュールの名前"` 2777 | 2778 | NOTE: Rakuのモジュールの一覧を見るには次のURLを参照してください: https://modules.perl6.org/ 2779 | 2780 | === モジュールの使用 2781 | MD5は128ビットのハッシュ値を生成する暗号学的ハッシュ関数です。 + 2782 | MD5は、データベースに格納されているパスワードの暗号化など様々なアプリケーションで使われています。 2783 | 新たなユーザーが登録されるとき、資格情報は平文として保存されずに _ハッシュ_ 化されます。 2784 | この背景にある根拠は、もしDBがハッキングの被害にあっていたとしても、攻撃者はパスワードが何であるかを知ることができないということです。 2785 | 2786 | 2787 | 幸運なことに、MD5アルゴリズムを実装する必要はありません。MD5アルゴリズムを実装したRakuモジュールがすでにあります。 + 2788 | インストールしましょう: + 2789 | `zef install Digest::MD5` 2790 | 2791 | では、次のスクリプトを実行してください: 2792 | [source,perl6] 2793 | ---- 2794 | use Digest::MD5; 2795 | my $password = "password123"; 2796 | my $hashed-password = Digest::MD5.new.md5_hex($password); 2797 | 2798 | say $hashed-password; 2799 | ---- 2800 | ハッシュを生成する `md5_hex()` 関数を実行するために、この関数の実行に必要なモジュールをロードしなくてはなりません。 + 2801 | `use` キーワードはスクリプトの中で使いたいモジュールをロードします。 2802 | 2803 | WARNING: 実用的にはMD5ハッシュ単独では不十分です、なぜなら辞書攻撃を受けやすいからです。 + 2804 | サルトと組み合わせるべきです。 link:https://en.wikipedia.org/wiki/Salt_(cryptography)[https://en.wikipedia.org/wiki/Salt_(cryptography)]. 2805 | 2806 | == ユニコード 2807 | 2808 | ユニコードは標準的なエンコーディングで、世界のほとんどの書込システムにおいてテキストを表現します。 + 2809 | UTF-8は、ユニコードにおける、すべての文字や符号位置をエンコーディングすることができる文字エンコードです。 2810 | 2811 | 文字は次によって定義されます: + 2812 | *書記素*: 視覚的表現。 + 2813 | *符号位置*: 文字に割り当てられた数。 + 2814 | *符号位置の名前*: 文字に割り当てられた名前。 2815 | 2816 | === ユニコードの使用 2817 | 2818 | .ユニコードを使ってどうやって文字を出力することができるのか見てみましょう 2819 | [source,perl6] 2820 | ---- 2821 | say "a"; 2822 | say "\x0061"; 2823 | say "\c[LATIN SMALL LETTER A]"; 2824 | ---- 2825 | 上記の三つの行は文字を作るためにそれぞれ異なった方法をとっています: 2826 | 2827 | . 直接文字を書く (書記素) 2828 | 2829 | . `\x` と符号位置を使う 2830 | 2831 | . `\c` と符号位置の名前を使う 2832 | 2833 | .では、スマイリーを出力してみましょう 2834 | [source,perl6] 2835 | ---- 2836 | say "☺"; 2837 | say "\x263a"; 2838 | say "\c[WHITE SMILING FACE]"; 2839 | ---- 2840 | 2841 | .二つの符号位置を組み合わせている例です 2842 | [source,perl6] 2843 | ---- 2844 | say "á"; 2845 | say "\x00e1"; 2846 | say "\x0061\x0301"; 2847 | say "\c[LATIN SMALL LETTER A WITH ACUTE]"; 2848 | ---- 2849 | 2850 | `a` は次のように書けます: 2851 | 2852 | * ユニークな符号位置 `\x00e1` を使う 2853 | 2854 | * もしくは `a` とアキュート・アクセントの符号位置を組み合わせる `\x0061\x0301` 2855 | 2856 | .いくつかのメソッドを使うことができます: 2857 | [source,perl6] 2858 | ---- 2859 | say "á".NFC; 2860 | say "á".NFD; 2861 | say "á".uniname; 2862 | ---- 2863 | 2864 | .`出力` 2865 | ---- 2866 | NFC:0x<00e1> 2867 | NFD:0x<0061 0301> 2868 | LATIN SMALL LETTER A WITH ACUTE 2869 | ---- 2870 | 2871 | `NFC` はユニークな符号位置を返します。 + 2872 | `NFD` は文字を分解し、それぞれの符号位置を返します。 + 2873 | `uniname` は符号位置の名前を返します。 2874 | 2875 | .ユニコード文字は識別子として用いることができます: 2876 | [source,perl6] 2877 | ---- 2878 | my $Δ = 1; 2879 | $Δ++; 2880 | say $Δ; 2881 | ---- 2882 | 2883 | .ユニコード文字で算数をすることができます: 2884 | [source,perl6] 2885 | ---- 2886 | my $var = 2 + ⅒; 2887 | say $var; 2888 | ---- 2889 | 2890 | === ユニコードを考慮した操作 2891 | 2892 | ==== 数値 2893 | 2894 | アラビア数字は十個あります: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 2895 | この数字は世界でもっとも使われているものです。 2896 | 2897 | とはいえ、世界には異なる数字を使うような少数派の地域があります。 2898 | 2899 | アラビア数字以外の数字を使うときに特別な注意は必要ありません。 2900 | すべてのメソッド/演算子は期待通りに動きます。 2901 | 2902 | [source,perl6] 2903 | ---- 2904 | say (٤,٥,٦,1,2,3).sort; # (1 2 3 4 5 6) 2905 | say 1 + ٩; # 10 2906 | ---- 2907 | 2908 | ==== 文字列 2909 | 一般的な文字列操作をしようとしたとき、特に比較やソートのときに、常に求めていた結果を得ることができるわけではないかもしれません。 2910 | 2911 | ===== 比較 2912 | 2913 | [source,perl6] 2914 | ---- 2915 | say 'a' cmp 'B'; # More 2916 | ---- 2917 | 上記の例では `a` は `B` よりも大きいということを示しています。 2918 | 小文字の `a` の符号位置は大文字の `B` の符号位置よりも大きいというのがその理由です。 2919 | 2920 | 技術的には正しい一方、ひょっとしたら求めていたものとは違うかもしれません。 2921 | 2922 | 幸運なことにRakuは link:http://unicode.org/reports/tr10/[ユニコード照合アルゴリズム] を実装したメソッド/演算子を持っています。 + 2923 | その一つが `unicmp` です。これは上記で示した `cmp` のようにふるまいますがこのアルゴリズムを使用する点が異なります。 2924 | 2925 | [source,perl6] 2926 | ---- 2927 | say 'a' unicmp 'B'; # Less 2928 | ---- 2929 | 2930 | 見てわかる通り、 `unicmp` 演算子を使うことで `a` は `B` より小さいという期待通りの結果が得られました。 2931 | 2932 | ===== ソート 2933 | 符号位置を利用したソートにおいて `sort` メソッドの他の選択肢として、Rakuは link:http://unicode.org/reports/tr10/[ユニコード照合アルゴリズム] を実装した `collate` メソッドを提供しています。 2934 | 2935 | [source,perl6] 2936 | ---- 2937 | say ('a','b','c','D','E','F').sort; # (D E F a b c) 2938 | say ('a','b','c','D','E','F').collate; # (a b c D E F) 2939 | ---- 2940 | 2941 | == 並列処理、並行性、非同期性 2942 | 2943 | === 並列処理 2944 | 一般的な状況では、プログラムのすべてのタスクは上から順に実行されます。 + 2945 | もし、たくさんの時間がかかるようなことを行おうとしているのでないかぎりは問題にはなりません。 2946 | 2947 | ありがたいことに、Rakuには並列実行のための機能があります。 + 2948 | 現在のところ、次の二つの事柄のうちの一つを意味するということを頭にとどめておくことが重要です: 2949 | 2950 | * *タスクの並列処理*: 二つ(もしくはそれ以上)の独立した式が並列実行されます。 2951 | * *データの並列処理*: 一つの式が要素のリストに対して並列的に反復処理を行います。 2952 | 2953 | まずは後者の方から始めましょう. 2954 | 2955 | ==== データの並列処理 2956 | [source,perl6] 2957 | ---- 2958 | my @array = 0..50000; # 配列の作成 2959 | my @result = @array.map({ is-prime $_ }); # それぞれの配列の要素に対して is-prime を呼ぶ 2960 | say now - INIT now; # スクリプトの処理が完了するまでにかかる時間を出力 2961 | ---- 2962 | 2963 | .上記の例について考えてみましょう: 2964 | `@array.map({ is-prime $_ })` という操作を行っているだけです + 2965 | 配列のそれぞれの要素に対して `is-prime` サブルーチンが経時的に呼び出されています: 2966 | `is-prime @array[0]`、`is-prime @array[1]` 、`is-prime @array[2]` ・・・の順です 2967 | 2968 | .幸運なことに `is-prime` を複数の配列の要素に対して同時に呼び出すことができます: 2969 | [source,perl6] 2970 | ---- 2971 | my @array = 0..50000; # 配列の作成 2972 | my @result = @array.race.map({ is-prime $_ }); # それぞれの配列の要素に対して is-prime を呼ぶ 2973 | say now - INIT now; # スクリプトの処理が完了するまでにかかる時間を出力 2974 | ---- 2975 | 2976 | 式の中で `race` を使用していることに注目してください。 2977 | このメソッドは配列に対する並列的な反復処理を可能にします。 2978 | 2979 | 両方の例( `race` の有る方と無い方)を実行したのち、両方のスクリプトにおいて処理が完了するのにかかる時間を比べてください。 2980 | 2981 | [TIP] 2982 | ==== 2983 | `race` は要素の順番を保ちません。もし要素の順番を保ちたいのであれば、代わりに `hyper` を用いてください。 2984 | 2985 | [source,perl6] 2986 | .race 2987 | ---- 2988 | my @array = 1..1000; 2989 | my @result = @array.race.map( {$_ + 1} ); 2990 | .say for @result; 2991 | ---- 2992 | 2993 | [source,perl6] 2994 | .hyper 2995 | ---- 2996 | my @array = 1..1000; 2997 | my @result = @array.hyper.map( {$_ + 1} ); 2998 | .say for @result; 2999 | ---- 3000 | 3001 | もし両方の例を実行したなら、片方はソートされていてもう片方はソートされていないことに気づいたはずです。 3002 | 3003 | ==== 3004 | 3005 | ==== タスクの並列処理 3006 | 3007 | [source,perl6] 3008 | ---- 3009 | my @array1 = 0..49999; 3010 | my @array2 = 2..50001; 3011 | 3012 | my @result1 = @array1.map( {is-prime($_ + 1)} ); 3013 | my @result2 = @array2.map( {is-prime($_ - 1)} ); 3014 | 3015 | say @result1 eqv @result2; 3016 | 3017 | say now - INIT now; 3018 | ---- 3019 | 3020 | .上記の例について考えてみてください: 3021 | 3022 | . 二つの配列を定義しました 3023 | 3024 | . それぞれの配列に対して異なる操作を適用し、結果を保存しました 3025 | 3026 | . そして、両方の結果が同じであるかを調べました 3027 | 3028 | このスクリプトは `@array1.map( {is-prime($_ + 1)} )` が終了するのを待っています + 3029 | それから、`@array2.map( {is-prime($_ - 1)} )` を評価します。 3030 | 3031 | それぞれの配列に対して適用された操作の両方が互いに依存していません。 3032 | 3033 | .同時に実行してみたらどうでしょう? 3034 | [source,perl6] 3035 | ---- 3036 | my @array1 = 0..49999; 3037 | my @array2 = 2..50001; 3038 | 3039 | my $promise1 = start @array1.map( {is-prime($_ + 1)} ).eager; 3040 | my $promise2 = start @array2.map( {is-prime($_ - 1)} ).eager; 3041 | 3042 | my @result1 = await $promise1; 3043 | my @result2 = await $promise2; 3044 | 3045 | say @result1 eqv @result2; 3046 | 3047 | say now - INIT now; 3048 | ---- 3049 | 3050 | .説明 3051 | `start` サブルーチンはコードを評価し、`Promise型のオブジェクト` (端的には `約束` )を返します。 + 3052 | もしコードが正しく評価されたのなら _約束_ は *守られ* ているでしょう。 + 3053 | もしコードが例外を投げたのなら _約束_ は *破られ* ているでしょう。 3054 | 3055 | `await` サブルーチンは *約束* を待ちます。 + 3056 | もし約束が *守られた* なら返された値を取得するでしょう。 + 3057 | もし約束が *破られた* なら投げられた例外を取得するでしょう。 3058 | 3059 | それぞれのスクリプトにおいて処理が終了するのにかかった時間を調べてください。 3060 | 3061 | WARNING: 並列処理にはスレッディングのオーバーヘッドがあります。もしオーバーヘッドが計算速度で相殺されないのなら、スクリプトが遅くなってしまったように見えるでしょう。 + 3062 | これが `race` 、 `hyper` 、 `start` 、 `await` をいたってシンプルなスクリプトに対して用いると実際には遅くなってしまう理由です。 3063 | 3064 | === 並行性と非同期性 3065 | NOTE: 並行/非同期プログラミングについてもっと情報を知りたいのなら、次のURLを参照してください: https://docs.raku.org/language/concurrency 3066 | 3067 | == ネイティブコールインタフェース 3068 | 3069 | Rakuではネイティブコール (Native Call) インタフェースを用いて、Cのライブラリを使うことができます。 3070 | 3071 | `NativeCall` はRakuと一緒に提供されている標準モジュールです 3072 | 3073 | === 関数の呼び出し 3074 | 3075 | `hellofromc` という関数が定義されている下記コードについて考えてみましょう。 3076 | この関数はターミナルに `Hello from C` と表示します。 3077 | この関数は引数を受け付けませんし返り値もありません。 3078 | 3079 | [source,c] 3080 | .ncitest.c 3081 | ---- 3082 | #include 3083 | 3084 | void hellofromc () { 3085 | printf("Hello from C\n"); 3086 | } 3087 | ---- 3088 | 3089 | OSに応じて、次のコマンドを実行し、上記のCのコードをライブラリへとコンパイルしてください。 3090 | 3091 | .Linux: 3092 | ---- 3093 | gcc -c -fpic ncitest.c 3094 | gcc -shared -o libncitest.so ncitest.o 3095 | ---- 3096 | 3097 | .Windows: 3098 | ---- 3099 | gcc -c ncitest.c 3100 | gcc -shared -o ncitest.dll ncitest.o 3101 | ---- 3102 | 3103 | .On macOS: 3104 | ---- 3105 | gcc -dynamiclib -o libncitest.dylib ncitest.c 3106 | ---- 3107 | 3108 | Cのライブラリをコンパイルしたのと同じディレクトリで、次のコードを含むRakuのファイルを作り、実行してください。 3109 | 3110 | [source,perl6] 3111 | .ncitest.raku 3112 | ---- 3113 | use NativeCall; 3114 | 3115 | constant LIBPATH = "$*CWD/ncitest"; 3116 | sub hellofromc() is native(LIBPATH) { * } 3117 | 3118 | hellofromc(); 3119 | ---- 3120 | 3121 | .説明: 3122 | まずはじめに、 `NativeCall` モジュールを使うということを宣言しました。 + 3123 | 次に、Cのライブラリへのパスを保持している定数 `LIBPATH` を作りました。 + 3124 | `$*CWD` はカレントディレクトリを返すということに注意してください。 + 3125 | それから、 `hellofromc()` という新しいRakuのサブルーチンを作りました。 3126 | このサブルーチンは、`LIBPATH` 下のCのライブラリの同じ名前を持った対応する関数のラッパーとしてふるまうでしょう。 + 3127 | `is native` トレイトを用いることでこれを実現できます。 + 3128 | さいごにRakuのサブルーチンを呼び出しました。 + 3129 | 3130 | 突き詰めると、すべては `is native` を使用して、Cのライブラリと同じ名前を持つサブルーチンを宣言することに帰着します。 3131 | 3132 | === 関数の名前の変更 3133 | 3134 | 前節では、`is native` トレイトを使って同じ名前を持ったRakuのサブルーチンでラップすることで、どのようにしてCの関数を呼び出せるかを見てきました。 3135 | 3136 | 場合によっては、Rakuのサブルーチンの名前を変えたくなるかもしれません。 + 3137 | そうしたいときは、 `is symbol` トレイトを使います。 3138 | 3139 | 上記のRakuのスクリプトを変更し、 Rakuのサブルーチンの名前を `hellofromc` の代わりに `hello` と付けてみましょう。 3140 | 3141 | [source,perl6] 3142 | .ncitest.raku 3143 | ---- 3144 | use NativeCall; 3145 | 3146 | constant LIBPATH = "$*CWD/ncitest"; 3147 | sub hello() is native(LIBPATH) is symbol('hellofromc') { * } 3148 | 3149 | hello(); 3150 | ---- 3151 | 3152 | .説明: 3153 | この場合RakuのサブルーチンはCの対応する関数と異なる名前を持っています。 3154 | 元のCの関数の名前を用いて `is symbol` トレイトを使うべきです。 3155 | 3156 | === 引数渡し 3157 | 3158 | 次の改変されたCのライブラリをコンパイルし、その下のRakuのスクリプトを実行しましょう。 + 3159 | どのようにしてCとRakuのコードの両方を、文字列を受け取るために改変したかについて注目してください。 (Cでは `char*` で、Rakuでは `Str` です) 3160 | 3161 | [source,c] 3162 | .ncitest.c 3163 | ---- 3164 | #include 3165 | 3166 | void hellofromc (char* name) { 3167 | printf("Hello, %s! This is C!\n", name); 3168 | } 3169 | ---- 3170 | 3171 | [source,perl6] 3172 | .ncitest.raku 3173 | ---- 3174 | use NativeCall; 3175 | 3176 | constant LIBPATH = "$*CWD/ncitest"; 3177 | sub hello(Str) is native(LIBPATH) is symbol('hellofromc') { * } 3178 | 3179 | hello('Jane'); 3180 | ---- 3181 | 3182 | === 値の返却 3183 | 3184 | さっきと同じことをもう一度行い、ふたつの整数を受け取って足し合わせる単純な計算機を生成しましょう。 + 3185 | CのライブラリをコンパイルしてRakuのスクリプトを実行してください。 3186 | 3187 | [source,c] 3188 | .ncitest.c 3189 | ---- 3190 | int add (int a, int b) { 3191 | return (a + b); 3192 | } 3193 | ---- 3194 | 3195 | [source,perl6] 3196 | .ncitest.raku 3197 | ---- 3198 | use NativeCall; 3199 | 3200 | constant LIBPATH = "$*CWD/ncitest"; 3201 | sub add(int32,int32 --> int32) is native(LIBPATH) { * } 3202 | 3203 | say add(2,3); 3204 | ---- 3205 | 3206 | どのようにしてCとRakuの関数の両方がふたつの整数を受け取りひとつの整数を返しているかについて注目してください。 ( Cでは `int` でRakuでは `int32` です) 3207 | 3208 | === 型 3209 | 3210 | もしかしたら先ほどのRakuのスクリプトで `Int` ではなくて `int32` を使ったのはなぜなのか疑問に思ったかもしれません。 + 3211 | `Int`, `Rat` などといったいくつかのRakuの型はCの関数からの値を通して受け取るときにそのままでは使うことができません。 + 3212 | Rakuで使う型はCの型と同じでなければなりません。 3213 | 3214 | 幸運なことに、RakuはCでその型に相当する型と対応付けられた型を提供しています。 3215 | 3216 | [cols="^.^,^.^",options="header"] 3217 | |=== 3218 | 3219 | | Cの型 | Rakuの型 3220 | 3221 | | `char` .2+| `int8` 3222 | 3223 | | `int8_t` 3224 | 3225 | | `short` .2+| `int16` 3226 | 3227 | | `int16_t` 3228 | 3229 | | `int` .2+| `int32` 3230 | 3231 | | `int32_t` 3232 | 3233 | | `int64_t` | `int64` 3234 | 3235 | | `unsigned char` .2+| `uint8` 3236 | 3237 | | `uint8_t` 3238 | 3239 | | `unsigned short` .2+| `uint16` 3240 | 3241 | | `uint16_t` 3242 | 3243 | | `unsigned int` .2+| `uint32` 3244 | 3245 | | `uint32_t` 3246 | 3247 | | `uint64_t` | `uint64` 3248 | 3249 | | `long` | `long` 3250 | 3251 | | `long long` | `longlong` 3252 | 3253 | | `float` | `num32` 3254 | 3255 | | `double` | `num64` 3256 | 3257 | | `size_t` | `size_t` 3258 | 3259 | | `bool` | `bool` 3260 | 3261 | | `char*` (String) | `Str` 3262 | 3263 | | 配列: 例えば `int*` (intの配列) や `double*` (doubleの配列) | `CArray`: 例えば `CArray[int32]` や `CArray[num64]` 3264 | 3265 | |=== 3266 | 3267 | NOTE: ネイティブコール インタフェースについてより詳しく知りたい方は、次のURLを参照してください https://docs.raku.org/language/nativecall 3268 | 3269 | == コミュニティ 3270 | 3271 | * link:https://web.libera.chat/#raku[#raku] IRCチャンネルです。活発な議論が行われています。何でも気軽に質問してください。簡単でよいからすぐに回答がほしい場合に適しています。: https://raku.org/community/irc 3272 | 3273 | * link:https://stackoverflow.com/questions/tagged/raku[StackOverflow Raku questions] はIRCよりももっと詳しい回答がほしい場合に適しています。 3274 | 3275 | * link:https://rakudoweekly.wordpress.com[rakudoweekly] Rakudoとその周辺の出来事について今週のダイジェストをお伝えします。 3276 | 3277 | * link:http://pl6anet.org[pl6anet] Rakuのブログを集約しています。Rakuにフォーカスしたブログ記事にこうご期待ください。 3278 | 3279 | * link:https://www.reddit.com/r/rakulang/[/r/rakulang] Rakuのsubredditを購読しましょう。 3280 | 3281 | * link:https://twitter.com/raku_news[@raku_news] twitterでコミュニティをフォローしましょう。 3282 | -------------------------------------------------------------------------------- /uk.rakuguide.adoc: -------------------------------------------------------------------------------- 1 | = Вступ до Raku 2 | Naoum Hankache ; Dmytro Iaskolko ; 3 | :description: Загальна інтродукція до Raku 4 | :keywords: perl6, Raku, introduction, perl6intro, Raku introduction, Raku tutorial, Raku intro, raku, raku introduction, raku guide, raku tutorial, вступ, введення до Raku, інтродукція до 6, вивчення perl6 5 | :Email: naoum@hankache.com 6 | :Revision: 1.0 7 | :icons: font 8 | :source-highlighter: pygments 9 | //:pygments-style: manni 10 | :source-language: perl6 11 | :pygments-linenums-mode: table 12 | :toc: left 13 | :toc-title: Зміст 14 | :doctype: book 15 | :lang: uk 16 | 17 | Цей документ має на меті надати швидкий огляд мови програмування Raku. 18 | Новачкам у Raku цей документ надасть можливість почати вивчення та виркористання цієї мови. 19 | 20 | Деякі розділи цього документа посилаються на інші (більш досконалі та точні) частини https://docs.raku.org[документації до Raku]. 21 | Вам слід прочитати їх, якщо потребуєте більше інформації на окремі теми. 22 | 23 | Всюди у цьому документі ви знайдете приклади до найбільш обговорюваних тем. Аби краще їх зрозуміти, будь ласка, знайдіть трохи часу аби відтворити їх. 24 | 25 | .Ліцензії 26 | Цей документ ліцензовано за міжнародною ліцензією Creative Commons Attribution Share-Alike 4.0 (Із зазначенням авторства — Розповсюдження на тих самих умовах). 27 | Аби побачити копію цієї лізензії, перейдіть за посиланням: 28 | 29 | * https://creativecommons.org/licenses/by-sa/4.0/. 30 | 31 | .Долучення 32 | Якщо ви хочете долучитися до вдосконалення цього документа, перейдіть до: 33 | 34 | * https://github.com/hankache/rakuguide 35 | 36 | .Відгуки 37 | Будь-які відгуки вдячно приймають на: 38 | 39 | * naoum@hankache.com - English 40 | * mescalito.ua@gmail.com - Українська 41 | 42 | Якщо вам сподобалася ця робота, позначте зірочкою репозиторій за посиланням: link:https://github.com/hankache/rakuguide[Github]. 43 | 44 | .Переклади 45 | * Болгарський: https://raku.guide/bg 46 | * Китайський: https://raku.guide/zh 47 | * Голландський: https://raku.guide/nl 48 | * Французький: https://raku.guide/fr 49 | * Німецький: https://raku.guide/de 50 | * Індонезійський: https://raku.guide/id 51 | * Італійський https://raku.guide/it 52 | * Японський: https://raku.guide/ja 53 | * Португальський: https://raku.guide/pt 54 | * Іспаньский: https://raku.guide/es 55 | * Турецький: https://raku.guide/tr 56 | * Украінський: https://raku.guide/uk 57 | 58 | :sectnums: 59 | 60 | == Вступ 61 | === Що таке Raku 62 | Raku це високорівнева мова програмування загального призначення з поступовою типізацією. 63 | Raku є багатопарадигмальною мовою, яка підтримує процедурне, об'єктно-орієнтоване та функціональне програмування. 64 | 65 | .Гасло Raku: 66 | * ІБНОСЗЦ Існує більш ніж один спосіб зробити це. TMTOWTDI (Вимовляється Tiм Tоуді). 67 | * Прості речі мають лишатися простими, складні мають простішати, а неможливі мають стати складними. 68 | 69 | === Визначення 70 | * *Raku*: Позначення мови програмування в межах добірки тестів. 71 | Реалізації, які проходять добірку тестів без помилок, вважаються Raku. 72 | * *Rakudo*: Компілятор Raku. 73 | * *Rakudobrew*: менеджер встановлення Rakudo. 74 | * *Zef*: менеджер модулів Raku. 75 | * *Rakudo Star*: колекція, яка містить Rakudo, Zef, добірку модулів Raku та документацію. 76 | 77 | === Встановлення Raku. 78 | .Linux 79 | 80 | Аби встановити Rakudo Star, виконайте наступні команди у своєму терміналі: 81 | ---- 82 | mkdir ~/rakudo && cd $_ 83 | curl -LJO https://rakudo.org/latest/star/src 84 | tar -xzf rakudo-star-*.tar.gz 85 | mv rakudo-star-*/* . 86 | rm -fr rakudo-star-* 87 | 88 | ./bin/rstar install 89 | 90 | echo "export PATH=$(pwd)/bin/:$(pwd)/share/perl6/site/bin:$(pwd)/share/perl6/vendor/bin:$(pwd)/share/perl6/core/bin:\$PATH" >> ~/.bashrc 91 | source ~/.bashrc 92 | ---- 93 | Аби дізнатися про інші способи, перейдіть до https://rakudo.org/star/source 94 | 95 | .macOS 96 | Доступними є чотири варіанти: 97 | 98 | * Дотримуватися тих самих кроків, що й для Linux 99 | * Встановити через homebrew: `brew install rakudo-star` 100 | * Встановити з MacPorts: `sudo port install rakudo` 101 | * Завантажити найсвіжіший встановлювач з https://rakudo.perl6.org/downloads/star/ (файл з розширенням .dmg) 102 | 103 | .Windows 104 | . Завантажте останній встановлювач (файл з розширенням .msi) з https://rakudo.perl6.org/downloads/star/ + 105 | Якщо у вас 32-бітна система, завантажте файл х86; якщо 64-бітна, файл х86_64. 106 | . Піcля встановлення переконайтеся, що `C:\rakudo\bin` додано до змінної PATH. 107 | 108 | .Docker 109 | . Отримайте офіційний образ для Docker `docker pull rakudo-star` 110 | . Далі запустіть контейнер з цим образом `docker run -it rakudo-star` 111 | 112 | === Виконання коду Raku 113 | 114 | Виконувати код Raku можна в режимі інтерактивного інтерпретатора команд або REPL (Read-Eval-Print Loop). Для цього відкрийте вікно термінала, наберіть `raku` та натисніть [Enter]. Це призведе до появи запрошення `>`. Далі, наберіть рядок коду та натисніть [Enter], інтерпретатор надрукує значення або результат виконання цього рядка. Далі ви можете ввести інший рядок, або набрати `exit` та натиснути [Enter] аби завершити сесію інтерпретатора. 115 | 116 | Також ви можете записати свій код у файл, зберегти та виконати його. Є рекомендованим надавати скриптам Raku розширення `.raku`. Виконати такий файл можна набравши `raku ваш_скрипт.raku` у термінальному вікні та натиснувши [Enter]. На відміну від інтерактивного режиму це не призведе до негайного друку результатів виконання коду: код має містити команди на кшталт `say` аби надрукувати результати виконання. 117 | 118 | Інтерактивний режим здебільшого вживають, коли треба виконати якийсь конкретний фрагмент коду, зазвичай єдиний рядок. Програми більші за один рядое краще зберігати у файл і потім виконувати їх. 119 | 120 | Один рядок можна також виконати з командного рядка в неінтерактивному режимі, написавши `raku -e 'ваш код тут'` та натиснувши [Enter]. 121 | 122 | [TIP] 123 | -- 124 | Rakudo Star вже містить редактор, який допоможе вам отримати якнайбільше від інтерактивного режиму. 125 | 126 | Якщо ви встановили звичайний Rakudo замість Rakudo Star, тоді ви, можливо, не маєте змоги редагувати рядки (стрілки вгору та вниз для навігації по історії, ліворуч та праворуч для редагування поточного рядку, TAB для автодоповненя). Виконайте наступні команди, аби отримати все це: 127 | 128 | * `zef install Linenoise` спрацює на Windows, Linux та MacOS 129 | * `zef install Readline` якщо у вас Linux та ви полюбляєте бібліотеку _Readline_ 130 | -- 131 | 132 | === Текстові редактори 133 | 134 | Оскільки більшість часу ми писатимемо та зберігатимемо наші програми Raku у файлах, нам стане у пригоді будь-який пристойний текстовий редактор, який розуміє синтаксис Raku. 135 | 136 | Особисто я надаю перевагу http://www.vim.org/[Vim], автор оригінального (англомовного) тексту використовує https://atom.io/[Atom] - це модерні текстові редактори, які вміють підсвічувати синтаксис Raku одразу після встановлення. https://atom.io/packages/language-perl6[Raku FE] це альтернативний плагін для підсвічування синтаксису, який походить від оригінального пакету, але містить багато виправлень та доповнень. 137 | 138 | Інші люди у спільноті користуються https://www.gnu.org/software/emacs/[Emacs] чи http://padre.perlide.org/[Padre]. 139 | 140 | Свіжі версії Vim розуміють синтаксис Raku одразу після встановлення, Emacs та Padre потребують встановлення додаткових пакетів. 141 | 142 | 143 | === Вітаю Світ! 144 | 145 | Ми почнемо з ритуалу `Вітаю світ`. 146 | 147 | [source,perl6] 148 | say 'Вітання Світові!'; 149 | 150 | Це також може бути написане як 151 | 152 | [source,perl6] 153 | 'Вітаю світ!'.say; 154 | 155 | === Огляд синтаксису 156 | 157 | Raku є *вільною за формою* мовою: більшість часу ви можете використовувати довільну кількість пробілів, проте у певних випадках пробіл має значення. 158 | 159 | *Твердження* це, зазвичай, логічний рядок коду, який має закінчуватися крапкою з комою: 160 | [source,perl6] 161 | ---- 162 | say "Hello" if True; 163 | say "World" if False; 164 | ---- 165 | 166 | Крапка з комою не є обовʼязковою після останнього твердження у файлі чи блоку коду, але є доброю практикою додавати її у будь-якому випадку. 167 | 168 | *Блоки* можуть складатися з набору тверджень. Візьміть твердження у фігурні скобки аби створити блок: 169 | [source,perl6] 170 | ---- 171 | { 172 | say "Перше твердження у блоці."; 173 | say "Друге треврдження у блоці."; 174 | } 175 | ---- 176 | 177 | *Вираз* це спеціальний тип твердження, який повертає значення: 178 | `1+2` поверне `3` 179 | 180 | *Значення* бувають: 181 | 182 | * *Змінними*: це значення, якими можна керувати та їх міняти. 183 | * *Літералами*: це сталі значення, як число чи рядок. 184 | 185 | *Оператори* класифіковані за типами: 186 | 187 | |=== 188 | 189 | | *Тип* | *Пояснення* | *Приклад* 190 | 191 | | Префіксні | Перед значенням | ++1 192 | 193 | | Інфіксні | Між значеннями | 1+2 194 | 195 | | Постфіксні | Після значення | 1++ 196 | 197 | | Контейнерні | Навколо значення | (1) 198 | 199 | | Постконтейнерні | Після значення, навколо іншого | Array[1] 200 | 201 | |=== 202 | 203 | ==== Ідентифікатори 204 | 205 | Ідентифікатори - це імена, які ви даєте значенням, коли визначаете їх. 206 | 207 | .Правила: 208 | * Вони мають починатися з алфавітного символа, чи нижнього підкреслювання 209 | * Вони можуть містити числа, за винятком першого символа 210 | * Вони можуть містити дефіси та апострофи, (за винятком першої та останньої позиції) за умови, що праворуч від кожного дефіса чи апострофа знаходиться алфавітний символ. 211 | 212 | |=== 213 | 214 | | Вірно | Невірно 215 | 216 | | var1 | 1var 217 | 218 | | var-one | var-1 219 | 220 | | var'one | var'1 221 | 222 | | var1_ | var1' 223 | 224 | | _var1 | -var 225 | 226 | | змінна1 | 1змінна 227 | 228 | |=== 229 | 230 | .Угода іменування 231 | 232 | * Стиль верблюда: `variableNo1` 233 | 234 | * Шашличний стиль: `variable-no1` 235 | 236 | * Стиль змії: `variable_no1` 237 | 238 | Ви можете довільно іменувати ваші ідентифікатори, але ознакою гарного тону є використання якогось одного стилю. 239 | 240 | Використання осмислених назв полегшить ваше життя (та життя інших). 241 | 242 | * `var1 = var2 * var3` синтаксично вірно, але призначення кожної змінної не є очевидним. 243 | * `monthly-salary = daily-rate * working-days` значно кращій варіант іменування змінних. 244 | 245 | ==== Коментарі 246 | Коментар - це текст, ігнорований компілятором, який слугує для пояснення (саме пояснення, а не цитування) коду. 247 | 248 | Коментарі поділяють на три типи: 249 | 250 | * Однорядкові: 251 | [source,perl6] 252 | # Це коментар в один рядок 253 | 254 | * Вбудовані: 255 | [source,perl6] 256 | say #`(Це вбудований коментар) "Hello World." 257 | 258 | * Багаторядкові: 259 | [source,perl6] 260 | ----------------------------- 261 | =begin comment 262 | Це багаторядковий коментар. 263 | Коментар 1 264 | Коментар 2 265 | =end comment 266 | ----------------------------- 267 | 268 | ==== Лапки 269 | Рядки мають бути обмежені поодинокими чи подвійними лапками. 270 | 271 | Завжди використовуйте подвійні лапки якщо: 272 | 273 | * Ваш рядок містить апостроф. 274 | 275 | * Ваш рядок містить змінну, яку має бути розгорнуто. 276 | 277 | [source,perl6] 278 | ----------------------------------- 279 | say 'Вітаю, Світ'; # Вітаю, Світ 280 | say "Вітаю, світ"; # Вітаю, Світ 281 | say "Об'єм"; # Об'єм 282 | my $name = 'Андрій Кузьменко'; 283 | say 'Вітаю $name'; # Вітаю $name 284 | say "Вітаю $name"; # Вітаю Андрій Кузьменко 285 | ----------------------------------- 286 | 287 | == Оператори 288 | 289 | === Загальні оператори 290 | У таблиці нижче перераховані найбільш уживані оператори. 291 | [cols="^.^5m,^.^5m,.^20,.^20m,.^20m", options="header"] 292 | |=== 293 | 294 | | Оператор | Тип | Опис | Приклад | Результат 295 | 296 | | + | Інфіксний | Додавання | 1 + 2 | 3 297 | 298 | | - | Інфіксний | Віднімання | 3 - 1 | 2 299 | 300 | | * | Інфіксний | Множення | 3 * 2 | 6 301 | 302 | | ** | Інфіксний | Ступінь | 3 ** 2 | 9 303 | 304 | | / | Інфіксний | Ділення | 3 / 2 | 1.5 305 | 306 | | div | Інфіксний | Цілочисленне ділення (округлення до меншого) | 3 div 2 | 1 307 | 308 | | % | Інфіксний | Залишок від ділення | 7 % 4 | 3 309 | 310 | .2+| %% .2+| Інфіксний .2+| Ділимість | 6 %% 4 | False 311 | 312 | <| 6 %% 3 <| True 313 | 314 | | gcd | Інфіксний | Найбільший спільній дільник | 6 gcd 9 | 3 315 | 316 | | lcm | Інфіксний | Найменше спільне кратне | 6 lcm 9 | 18 317 | 318 | | == | Інфіксний | Арифметичне порівняння | 9 == 7 | False 319 | 320 | | != | Інфіксний | Арифметичне не дорівнює | 9 != 7 | True 321 | 322 | | < | Інфіксний | Менше | 9 < 7 | False 323 | 324 | | > | Інфіксний | Більше | 9 > 7 | True 325 | 326 | | \<= | Інфіксний | Менше чи дорівнює | 7 \<= 7 | True 327 | 328 | | >= | Інфіксний | Більше чи дорівнює | 9 >= 7 | True 329 | 330 | | eq | Інфіксний | Текстове порівняння | "John" eq "John" | True 331 | 332 | | ne | Інфіксний | Текстове не дорівнює | "John" ne "Jane" | True 333 | 334 | | = | Інфіксний | Привласнення | my $var = 7 | Присвоює значення `7` змінній `$var` 335 | 336 | .2+| ~ .2+| Інфіксний .2+| Злиття рядків | 9 ~ 7 | 97 337 | 338 | 'London',Ukraine => 'Kyiv'); 653 | say %capitals; 654 | ---- 655 | 656 | Деякі методи, які можуть бути застосовані до хешів: 657 | [source,perl6] 658 | .`Програма` 659 | ---- 660 | my %capitals = (UK => 'Лондон', Ukraine => 'Київ'); 661 | %capitals.push: (France => 'Париж'); 662 | say %capitals.kv; 663 | say %capitals.keys; 664 | say %capitals.values; 665 | say "Столиця Франції це: " ~ %capitals; 666 | ---- 667 | 668 | .`Вивід` 669 | ---- 670 | (France Париж UK Лондон Ukraine Київ) 671 | (France UK Ukraine) 672 | (Київ Лондон Париж) 673 | Столиця Франції це: Париж 674 | ---- 675 | 676 | .Пояснення 677 | `.push: (ключ => 'Значення')` додає нову пару ключ/значення. + 678 | `.kv` повертає список, який містить усі ключі та значення. + 679 | `.keys` повертає список, який містить усі ключі. + 680 | `.values` повертає список, який містить усі значення. + 681 | Ви можете звернутися до необхідного значення у хеші, вказавши його ключ `%hash<ключ>` 682 | 683 | NOTE: Повну довідку по хешам ви можете отримати тут: https://docs.raku.org/type/Hash 684 | 685 | === Типи 686 | У попередніх прикладах ми не вказували типи значень, які мають зберігати змінні. 687 | 688 | TIP: `.WHAT` поверне тип значення, збереженого у змінній. 689 | 690 | [source,perl6] 691 | ---- 692 | my $var = 'Text'; 693 | say $var; 694 | say $var.WHAT; 695 | 696 | $var = 123; 697 | say $var; 698 | say $var.WHAT; 699 | ---- 700 | 701 | Як бачимо з прикладу наведеного вище, тип значення у `$var` спочатку був (Str), потім став (Int). 702 | 703 | Такий стиль програмування називають динамічною типізацією. Динамічною в тому сенсі, що змінні можуть зберігати значення будь-якого типу. 704 | 705 | Тепер спробуйте виконати приклад ничже: + 706 | Зверніть увагу на `Int` перед ім'ям змінної. 707 | 708 | [source,perl6] 709 | ---- 710 | my Int $var = 'Text'; 711 | say $var; 712 | say $var.WHAT; 713 | ---- 714 | 715 | Цей код не буде виконано, натомість з'явиться помилка: `Type check failed in assignment to $var; expected Int but got Str` 716 | 717 | Відмінність у тому, що ми заздалегідь вказали, що змінна має бути типу (Int). 718 | Коли ми спробували присвоїти змінній значення типу (Str), компілятор повернув помилку. 719 | 720 | Такий стиль програмування називають статичною типізацією. Статичною в тому сенсі, що тип змінної визначають перед тим, як присвоїти значення і він не може бути змінений. 721 | 722 | Raku класифіковано як мову з *поступовою типізацією*, тобто вона дозволяє як *статичну*, так і *динамічну* типізацію. 723 | 724 | .Масиви та хеши також можуть бути статично типізованими: 725 | [source,perl6] 726 | ---- 727 | my Int @array = 1,2,3; 728 | say @array; 729 | say @array.WHAT; 730 | 731 | my Str @multilingual = "Hello","Вітаю","Hallo","您好","안녕하세요","こんにちは"; 732 | say @multilingual; 733 | say @multilingual.WHAT; 734 | 735 | my Str %capitals = (UK => 'London', Ukraine => 'Kyiv'); 736 | say %capitals; 737 | say %capitals.WHAT; 738 | 739 | my Int %country-codes = (UK => 44, Ukraine => 38); 740 | say %country-codes; 741 | say %country-codes.WHAT; 742 | ---- 743 | 744 | .У списку нижче ви можете бачити найчастіше уживані типи: 745 | Скоріш за все, ви ніколи не використаєте перші два - вони наведені для інформації. 746 | [cols="^.^1m,.^3m,.^2m,.^1m, options="header"] 747 | |=== 748 | 749 | | *Тип* | *Опис* | *Приклад* | *Результат* 750 | 751 | | Mu | Корінь ієрархії типів Raku | | 752 | 753 | | Any | Базовий клас за замовчуванням для усіх нових класів, та для більшості вбудованих класів | | 754 | 755 | | Cool | Значення, яке може бути рядком та цілим числом одночасно | my Cool $var = 31; say $var.flip; say $var * 2; | 13 62 756 | 757 | | Str | Рядок символів | my Str $var = "NEON"; say $var.flip; | NOEN 758 | 759 | | Int | Ціле число (довільної точності) | 7 + 7 | 14 760 | 761 | | Rat | Раціональне число (обмеженої точності) | 0.1 + 0.2 | 0.3 762 | 763 | | Bool | Логічне значення | !True | False 764 | 765 | |=== 766 | 767 | === Інтроспекція (відображення) 768 | 769 | Інтроспекція це процес отримання інформації про властивості об'єкта, такої як тип. + 770 | В одному з попередніх прикладів ми використали `.WHAT` аби отримати тип змінної. 771 | 772 | [source,perl6] 773 | ---- 774 | my Int $var; 775 | say $var.WHAT; # (Int) 776 | my $var2; 777 | say $var2.WHAT; # (Any) 778 | $var2 = 1; 779 | say $var2.WHAT; # (Int) 780 | $var2 = "Hello"; 781 | say $var2.WHAT; # (Str) 782 | $var2 = True; 783 | say $var2.WHAT; # (Bool) 784 | $var2 = Nil; 785 | say $var2.WHAT; # (Any) 786 | ---- 787 | 788 | Тип змінної, яка зберігає значення, має відношення до її значення. + 789 | Тип явно оголошеної пустої змінної є типом, з яким її оголосили. + 790 | Типом пустої змінної, тип якої не було оголошено явно, є `(Any)` + 791 | Аби очистити значення змінної, треба присвоїти їй значення `Nil`. 792 | 793 | === Область видимості 794 | 795 | Перш ніж вперше використати змінну, вона має бути оголошена. 796 | 797 | У Raku існує декілька способів це зробити. Досі ми використовували `my`. 798 | 799 | [source,perl6] 800 | my $var=1; 801 | 802 | Оголошення у вигляді `my` надає змінній область видимості. 803 | Іншими словами, змінна буде досяжна лише у тому блоці, де вона була оголошена. 804 | 805 | У Raku блок обмежений `{ }`. 806 | Якщо межі блоку не знайдені, змінна буде досяжною у всьому скрипті Raku. 807 | 808 | [source,perl6] 809 | ---- 810 | { 811 | my Str $var = 'Text'; 812 | say $var; # is accessible 813 | } 814 | say $var; # is not accessible, returns an error 815 | ---- 816 | 817 | Оскільки змінна досяжна лише у межах блоку, те саме ім'я змінної можна використати також в іншому блоці. 818 | 819 | [source,perl6] 820 | ---- 821 | { 822 | my Str $var = 'Text'; 823 | say $var; 824 | } 825 | my Int $var = 123; 826 | say $var; 827 | ---- 828 | 829 | === Привласнення чи зв'язування? 830 | У попередніх прикладах ми бачили як *привласнити* значення змінній. + 831 | *Привласнення* виконують за допомогою оператора `=`. 832 | [source,perl6] 833 | ---- 834 | my Int $var = 123; 835 | say $var; 836 | ---- 837 | 838 | Ми маємо змогу змінити значення, привласнене змінній: 839 | 840 | [source,perl6] 841 | .Привласнення 842 | ---- 843 | my Int $var = 123; 844 | say $var; 845 | $var = 999; 846 | say $var; 847 | ---- 848 | 849 | .`Виведення` 850 | ---- 851 | 123 852 | 999 853 | ---- 854 | 855 | З іншого боку, ми не можемо змінити значення, яке є *зв'язаним* зі змінною. + 856 | *Зв'язування* роблять за допомогою оператора `:=`. 857 | 858 | [source,perl6] 859 | .Зв'язування. 860 | ---- 861 | my Int $var := 123; 862 | say $var; 863 | $var = 999; 864 | say $var; 865 | ---- 866 | 867 | .`Виведення` 868 | ---- 869 | 123 870 | Cannot assign to an immutable value 871 | ---- 872 | 873 | [source,perl6] 874 | .Змінні можуть також бути зв'язаними з іншими змінними: 875 | ---- 876 | my $a; 877 | my $b; 878 | $b := $a; 879 | $a = 7; 880 | say $b; 881 | $b = 8; 882 | say $a; 883 | ---- 884 | 885 | .`Виведення` 886 | ---- 887 | 7 888 | 8 889 | ---- 890 | 891 | Зв'язування змінних є двонаправленим. + 892 | `$a := $b` та `$b := $a` мають однаковий ефект. 893 | 894 | NOTE: Аби дізнатися більше про змінні, завітайте до https://docs.raku.org/language/variables 895 | 896 | == Функції та мутатори 897 | 898 | Важливо відрізняти функції та мутатори. 899 | Функції не змінюють стан об'єкта, на якому їх було викликано. 900 | Мутатори змінюють стан об'єкта. 901 | 902 | [source,perl6,linenums] 903 | .`Скрипт` 904 | ---- 905 | my @numbers = [7,2,4,9,11,3]; 906 | 907 | @numbers.push(99); 908 | say @numbers; #1 909 | 910 | say @numbers.sort; #2 911 | say @numbers; #3 912 | 913 | @numbers.=sort; 914 | say @numbers; #4 915 | ---- 916 | 917 | .`Виведення` 918 | ---- 919 | [7 2 4 9 11 3 99] #1 920 | (2 3 4 7 9 11 99) #2 921 | [7 2 4 9 11 3 99] #3 922 | [2 3 4 7 9 11 99] #4 923 | ---- 924 | 925 | .Пояснення 926 | `.push` це мутатор, він змінює стан об'єкту (#1) 927 | 928 | `.sort` це функція; вона повертає відсортований масив, але не змінює стан вихідного масиву. 929 | 930 | * (#2) показує, що було повернено відсортований масив. 931 | 932 | * (#3) показує, що вихідний масив не було змінено. 933 | 934 | Аби змусити функцію поводитися, як мутатор, ми використовуємо `.=` замість `.` (#4) (Рядок 9 скрипта) 935 | 936 | == Цикли та умови 937 | Raku має багато варіантів будови умов та циклів. 938 | 939 | === if 940 | Код буде виконано лише у разі задовільнення умови; тобто вираз має повернути істину. 941 | 942 | [source,perl6] 943 | ---- 944 | my $age = 19; 945 | 946 | if $age > 18 { 947 | say 'Welcome' 948 | } 949 | ---- 950 | 951 | У Raku ми можемо поміняти місцями код та умову. + 952 | Навіть якщо код та умову було поміняно місцями, перевірка умови завжди буде виконана першою. 953 | 954 | [source,perl6] 955 | ---- 956 | my $age = 19; 957 | 958 | say 'Welcome' if $age > 18; 959 | ---- 960 | 961 | Якщо умову не було задовільнено, ми можемо вказати альтернативні блоки для виконання за допомогою 962 | 963 | * `else` 964 | * `elsif` 965 | 966 | [source,perl6] 967 | ---- 968 | # run the same code for different values of the variable 969 | my $number-of-seats = 9; 970 | 971 | if $number-of-seats <= 5 { 972 | say 'I am a sedan' 973 | } elsif $number-of-seats <= 7 { 974 | say 'I am 7 seater' 975 | } else { 976 | say 'I am a van' 977 | } 978 | ---- 979 | 980 | === unless 981 | Заперечна версія `if` ("Якщо не") може бути записана за допомогою `unless`. 982 | 983 | Наступний код: 984 | 985 | [source,perl6] 986 | ---- 987 | my $clean-shoes = False; 988 | 989 | if not $clean-shoes { 990 | say 'Clean your shoes' 991 | } 992 | ---- 993 | 994 | може бути записаний як: 995 | 996 | [source,perl6] 997 | ---- 998 | my $clean-shoes = False; 999 | 1000 | unless $clean-shoes { 1001 | say 'Clean your shoes' 1002 | } 1003 | ---- 1004 | 1005 | Заперечення у Raku виконують за допомогою `!` або `not`. 1006 | 1007 | `unless (умова)` використовують замість `if not (умова)`. 1008 | 1009 | `unless` не може мати блока `else`. 1010 | 1011 | === with 1012 | 1013 | `with` поводиться як `if`, але перевіряє чи визначена змінна. 1014 | 1015 | [source,perl6] 1016 | ---- 1017 | my Int $var=1; 1018 | 1019 | with $var { 1020 | say 'Hello' 1021 | } 1022 | ---- 1023 | 1024 | Якщо ви виконуєте код без присвоєння значення змінній, нічого не станеться. 1025 | [source,perl6] 1026 | ---- 1027 | my Int $var; 1028 | 1029 | with $var { 1030 | say 'Hello' 1031 | } 1032 | ---- 1033 | 1034 | `without` це заперечена версія `with`. Ви можете порівняти її з `unless`. 1035 | 1036 | Якщо першу умову `with` не було задовільнено, альтернативну путь може бути визначено за допомогою `orwith`. + 1037 | `with` та `orwith` можна порівняти з `if` та `elsif`. 1038 | 1039 | === for 1040 | 1041 | Цикл `for` використовують аби обходити значення у списках. 1042 | 1043 | [source,perl6] 1044 | ---- 1045 | my @array = [1,2,3]; 1046 | 1047 | for @array -> $array-item { 1048 | say $array-item * 100 1049 | } 1050 | ---- 1051 | 1052 | Зверніть увагу, що ми створили ітераційну змінну `$array-item` та виконали операцію `*100` з кожним елементом масиву. 1053 | 1054 | == given 1055 | 1056 | `given` у Raku - це еквівалент виразу `switch` у інших мовах, але набагато більш потужний. 1057 | 1058 | #TODO 1059 | -------------------------------------------------------------------------------- /zh.rakuguide.adoc: -------------------------------------------------------------------------------- 1 | = Raku 入门 2 | Naoum Hankache ; ohmycloud ; wenjie1991 3 | :description: Raku 入门 4 | :keywords: perl6, Raku, 入门, perl6intro, Raku 入门, Raku 指南, Raku Intro, Raku Guide 5 | :Revision: 1.0 6 | :icons: font 7 | :source-highlighter: pygments 8 | //:pygments-style: manni 9 | :source-language: perl6 10 | :pygments-linenums-mode: table 11 | :toc: left 12 | :doctype: book 13 | :lang: zh 14 | 15 | 本文档旨在为您提供 Raku 编程语言的快速概述。 16 | 它可以让 Raku 初学者快速上手。 17 | 18 | 本文档的一部分章节涉及 link:http://docs.raku.org[Raku 文档]的其他(更完整和准确)部分。 19 | 如果您需要有关特定主题的更多信息,您应该阅读它们。 20 | 21 | 在本文档中,您会找到大部分所讨论主题的示例。 22 | 为了更好地理解它们,请花时间再现所有的例子。 23 | 24 | .许可证 25 | 本工作根据知识共享署名 - 授权4.0国际许可协议进行许可。 26 | 要查看此许可证的副本,请访问 27 | 28 | * https://creativecommons.org/licenses/by-sa/4.0/. 29 | 30 | .贡献 31 | 如果您想对此文档做贡献,请访问: 32 | 33 | * https://github.com/hankache/rakuguide 34 | 35 | .意见反馈 36 | 欢迎所有的反馈: 37 | naoum@hankache.com 38 | 39 | 如果你喜欢这个工作, 欢迎在 40 | link:https://github.com/hankache/rakuguide[Github] 给这个仓库点赞。 41 | 42 | .翻译 43 | * 保加利亚语: https://raku.guide/bg 44 | * 中文: https://raku.guide/zh 45 | * 荷兰语: https://raku.guide/nl 46 | * 法语: https://raku.guide/fr 47 | * 德语: https://raku.guide/de 48 | * 印度尼西亚语: https://raku.guide/id 49 | * 意大利语 https://raku.guide/it 50 | * 日语: https://raku.guide/ja 51 | * 葡萄牙语: https://raku.guide/pt 52 | * 西班牙语: https://raku.guide/es 53 | * 土耳其语: https://raku.guide/tr 54 | * 俄语: https://raku.guide/ru 55 | * 烏克蘭: https://raku.guide/uk 56 | 57 | :sectnums: 58 | == 序言 59 | === 什么是 Raku 60 | Raku 是一种高级的,通用的,渐进类型的语言。 61 | Raku 是多范式的。它支持过程式编程,面向对象编程和函数式编程。 62 | 63 | .Raku motto: 64 | * TMTOWTDI (发音是 Tim Toady): 每个问题都有许多解决方式。 65 | * 简单的事情应该保持简单,困难的事情应该变得简单,不可能的事情应该成为可能。 66 | 67 | === 术语 68 | * *Raku*: 带有测试套件的语言规范。 69 | Raku 是通过该规范测试套件的实现。 70 | * *Rakudo*: Raku 的编译器。 71 | * *Rakudobrew*: Rakudo 的安装管理器。 72 | * *Zef*: Raku 的模块安装程序。 73 | * *Rakudo Star*: 是一个包含 Rakudo, Zef, 和经遴选的 Raku 模块与文档的分发包。 74 | 75 | === 安装 Raku 76 | .Linux 77 | . 安装 Rakudo Star: 在终端中运行: 78 | ---- 79 | mkdir ~/rakudo && cd $_ 80 | curl -LJO https://rakudo.org/latest/star/src 81 | tar -xzf rakudo-star-*.tar.gz 82 | mv rakudo-star-*/* . 83 | rm -fr rakudo-star-* 84 | 85 | ./bin/rstar install 86 | 87 | echo "export PATH=$(pwd)/bin/:$(pwd)/share/perl6/site/bin:$(pwd)/share/perl6/vendor/bin:$(pwd)/share/perl6/core/bin:\$PATH" >> ~/.bashrc 88 | source ~/.bashrc 89 | ---- 90 | 91 | 对于其它选项, 请参阅 https://rakudo.org/star/source 92 | 93 | .OSX 94 | 可用的选择有四种: 95 | 96 | * 按照在 Linux 上安装步骤进行同样的操作 97 | * 使用 homebrew 安装: `brew install rakudo-star` 98 | * 使用 MacPorts 安装: `sudo port install rakudo` 99 | * 从 https://rakudo.org/latest/star/macos 下载最新的安装器(.dmg 后缀的文件) 100 | 101 | .Windows 102 | . 从 或 下载最新的安装器(.msi 后缀的文件) + 103 | . 如果你的系统架构是 64-bit, 可以从 https://rakudo.org/latest/star/win 下载最新的安装器(.msi 后缀的文件)。 104 | . 安装完成后,确保 `C:\rakudo\bin` 在 PATH 中。 105 | 106 | .Docker 107 | . 获取官方的 Docker 镜像 `docker pull rakudo-star` 108 | . 然后运行一个带有该镜像的容器 `docker run -it rakudo-star` 109 | 110 | === 运行 Raku 代码 111 | 112 | 运行 Raku 代码可以通过使用 REPL(Read-Eval-Print 循环)来实现。 113 | 实现方法是,打开命令行终端,输入 `perl6` 后回车。这会导致命令提示符 `>` 的出现。接着, 输入一行代码后回车。 114 | REPL 将会打印出该行的返回值。然后你可以输入另外的行, 或输入 `exit` 并回车以离开 REPL。 115 | 116 | 或者,你可以在文件中编写你的代码,保存后再运行。建议 Raku 脚本文件的扩展名设定为 `.raku`。 117 | 运行该文件的代码时只需将 `perl6 filename.raku` 输入到命令行终端后回车即可。 118 | 但不像 REPL, 这不会自动打印出每一行的结果:这里要求文件中的代码必须包含 `say` 那样的语句以打印输出。 119 | 120 | REPL 大多用于测试特殊代码片段,通常只有有一行。对于超过一行的程序,建议先把它们保存到文件中而后再运行。 121 | 122 | 单行代码也通过在命令行中键入 `perl6 -e 'your code here'` 并回车来以非交互的方式来测试。 123 | 124 | [TIP] 125 | -- 126 | Rakudo Star 搭载了一个行编辑器来帮你最大程度地利用 REPL。 127 | 128 | 如果你安装了普通的 Rakudo 而不是 Rakudo Star,那么你的行编辑功能可能没有开启(利用上下方向键查询历史;左右方向键以编辑输入;TAB 键以完成当前行输入)功能。 129 | 此时可以考虑运行以下命令来设置好上述功能: 130 | 131 | * `zef install Linenoise` 能在 Windows, Linux 和 OSX 上工作 132 | 133 | * `zef install Readline` 如果你使用 Linux 并且偏好 _Readline_ 库 134 | -- 135 | 136 | === 编辑器 137 | 因为大多数情况下我们会在文件中编写并存储 Raku 程序,因此我们需要一个优雅的而且能识别 Raku 语法的文本编辑器。 138 | 139 | 从个人角度来说,我推荐正在使用的 https://atom.io/[Atom]。 140 | 这是一个时尚的文本编辑器,带有开箱即用的 Raku 语法高亮功能。 141 | https://atom.io/packages/language-perl6[Raku FE] 是相对于 Atom 默认 Raku 语法高亮插件的另一种选择,也可以高亮 Raku 的语法,该软件来源于原始的包, 142 | 但存在很多 bug 需要修补和编辑。 143 | 144 | 社区里的其他同伴也有使用 http://www.vim.org/[Vim] , https://www.gnu.org/software/emacs/[Emacs] 和 http://padre.perlide.org/[Padre] 的。 145 | 146 | 最新版本的 Vim 自带 Raku 语法高亮的功能,Emacs 和 Padre 则需要另行安装额外的包。 147 | 148 | 149 | === Hello World! 150 | 我们会从 `hello world` 惯例程序开始。 151 | 152 | [source,perl6] 153 | say 'hello world'; 154 | 155 | 它也可以被写为: 156 | 157 | [source,perl6] 158 | 'hello world'.say; 159 | 160 | === 语法概要 161 | Raku 是 *形式自由的*, (大多数时候)你可以使用任意数量的空格。 162 | 163 | *语句* 通常是一个逻辑代码行, 它们需要以一个分号结尾: 164 | `say "Hello" if True;` 165 | 166 | *表达式* 是一种能够返回值的特殊类型的语句: 167 | `1+2` 会返回 `3` 168 | 169 | 表达式由 *项* 和 *运算符* 组成。 170 | 171 | *项* 是: 172 | 173 | * *变量*: 一个可以被操作和改变的值。 174 | 175 | * *字面值*: 一个像数字或字符串那样的常量值。 176 | 177 | *运算符* 的分类: 178 | 179 | |=== 180 | 181 | | *类型* | *解释* | *示例* 182 | 183 | | 前缀 | 用在项前 | `++1` 184 | 185 | | 中缀 | 用在项与项之间 | `1+2` 186 | 187 | | 后缀 | 用在项后 | `1++` 188 | 189 | | 环缀 | 包围项 | `(1)` 190 | 191 | | 后环缀 | 在一个项之后, 包围另一个项 | `Array[1]` 192 | 193 | |=== 194 | 195 | ==== 标识符 196 | 标识符是你定义项时给它们起的名字。 197 | 198 | .命名规则: 199 | * 它们必须以字母字符或下划线开头。 200 | 201 | * 它们可以包含数字 (除了第一个字符)。 202 | 203 | * 它们可以包含破折号或撇号(除了第一个和最后一个字符), 前提是每个破折号或撇号的右侧有一个字母字符。 204 | 205 | |=== 206 | 207 | | *有效标识符* | *无效标识符* 208 | 209 | | `var1` | `1var` 210 | 211 | | `var-one` | `var-1` 212 | 213 | | `var'one` | `var'1` 214 | 215 | | `var1_` | `var1'` 216 | 217 | | `_var` | `-var` 218 | 219 | |=== 220 | 221 | .命名约定: 222 | * 驼峰式: `variableNo1` 223 | 224 | * 串联式: `variable-no1` 225 | 226 | * 蛇行式: `variable_no1` 227 | 228 | 你可以随意命名标识符,但是最好采用一个命名约定。 229 | 230 | 使用有意义的名称将减轻你(和其他人)的编程工作负担。 231 | 232 | * `var1 = var2 * var3` 在语法上是正确的,但它的意图不明显。 233 | * `monthly-salary = daily-rate * working-days` 是更好的命名变量的方法。 234 | 235 | ==== 注释 236 | 注释是用于注解的文本片段, 编译器会忽略注释。 237 | 238 | 注释被分为 3 种类型: 239 | 240 | * 单行注释: 241 | + 242 | [source,perl6] 243 | # This is a single line comment 244 | 245 | * 嵌套的注释: 246 | + 247 | [source,perl6] 248 | say #`(This is an embedded comment) "Hello World." 249 | 250 | * 多行注释: 251 | + 252 | [source,perl6] 253 | ----------------------------- 254 | =begin comment 255 | This is a multi line comment. 256 | Comment 1 257 | Comment 2 258 | =end comment 259 | ----------------------------- 260 | 261 | ==== 引号 262 | 字符串被双引号或单引号包围。 263 | 264 | 必须使用双引号, 如果: 265 | 266 | * 你的字符串包含单引号。 267 | 268 | * 你的字符串包含需要被替换的变量。 269 | 270 | [source,perl6] 271 | ----------------------------------- 272 | say 'Hello World'; # Hello World 273 | say "Hello World"; # Hello World 274 | say "Don't"; # Don't 275 | my $name = 'John Doe'; 276 | say 'Hello $name'; # Hello $name 277 | say "Hello $name"; # Hello John Doe 278 | ----------------------------------- 279 | 280 | == 运算符 281 | 282 | === 一般运算符 283 | 下表列出了最常用的运算符。 284 | [cols="^.^5m,^.^5m,.^20,.^20m,.^20m", options="header"] 285 | |=== 286 | 287 | | 运算符 | 类型 | 描述 | 例子 | 结果 288 | 289 | | + | 中缀 | 加 | 1 + 2 | 3 290 | 291 | | - | 中缀 | 减 | 3 - 1 | 2 292 | 293 | | * | 中缀 | 乘 | 3 * 2 | 6 294 | 295 | | ** | 中缀 | 指数 | 3 ** 2 | 9 296 | 297 | | / | 中缀 | 除 | 3 / 2 | 1.5 298 | 299 | .2+| div .2+| 中缀 .2+| 整除 (向下取整) | 3 div 2 | 1 300 | <| (-3) div 2 <| -2 301 | 302 | | % | 中缀 | 取模 | 7 % 4 | 3 303 | 304 | .2+| %% .2+| 中缀 .2+| 是否能整除 | 6 %% 4 | False 305 | 306 | <| 6 %% 3 <| True 307 | 308 | | gcd | 中缀 | 最大公约数 | 6 gcd 9 | 3 309 | 310 | | lcm | 中缀 | 最小公倍数 | 6 lcm 9 | 18 311 | 312 | | == | 中缀 | 数值相等 | 9 == 7 | False 313 | 314 | | != | 中缀 | 数值不等 | 9 != 7 | True 315 | 316 | | < | 中缀 | 数值小于 | 9 < 7 | False 317 | 318 | | > | 中缀 | 数值大于 | 9 > 7 | True 319 | 320 | | \<= | 中缀 | 数值小于等于 | 7 \<= 7 | True 321 | 322 | | >= | 中缀 | 数值大于等于 | 9 >= 7 | True 323 | 324 | .3+| +<=>+ .3+| 中缀 .3+| 数值比较 | 1 +<=>+ 1.0 | Same 325 | 326 | <| 1 +<=>+ 2 <| Less 327 | 328 | <| 3 +<=> 2+ <| More 329 | 330 | | eq | 中缀 | 字符串相等 | "John" eq "John" | True 331 | 332 | | ne | 中缀 | 字符串不等 | "John" ne "Jane" | True 333 | 334 | | lt | 中缀 | 字符串小于 | "a" lt "b" | True 335 | 336 | | gt | 中缀 | 字符串大于 | "a" gt "b" | False 337 | 338 | | le | 中缀 | 字符串小于等于 | "a" le "a" | True 339 | 340 | | ge | 中缀 | 字符串大于等于 | "a" ge "b" | False 341 | 342 | .3+| leg .3+| 中缀 .3+| 字符串比较 | "a" leg "a" | Same 343 | 344 | <| "a" leg "b" <| Less 345 | 346 | <| "c" leg "b" <| More 347 | 348 | .2+| cmp .2+| 中缀 .2+| 智能比较 | "a" cmp "b" | Less 349 | 350 | <| 3.5 cmp 2.6 <| More 351 | 352 | | = | 中缀 | 赋值 | my $var = 7 | 将字面值 `7` 赋值给变量 `$var` 353 | 354 | .2+| ~ .2+| 中缀 .2+| 字符串连接 | 9 ~ 7 | 97 355 | 356 | 'London', Germany => 'Berlin'); 663 | say %capitals; 664 | ---- 665 | 666 | 一些能应用于哈希的方法: 667 | [source,perl6] 668 | .`脚本` 669 | ---- 670 | my %capitals = (UK => 'London', Germany => 'Berlin'); 671 | %capitals.push: (France => 'Paris'); 672 | say %capitals.kv; 673 | say %capitals.keys; 674 | say %capitals.values; 675 | say "The capital of France is: " ~ %capitals; 676 | ---- 677 | 678 | .`输出` 679 | ---- 680 | (France Paris Germany Berlin UK London) 681 | (France Germany UK) 682 | (Paris Berlin London) 683 | The capital of France is: Paris 684 | ---- 685 | 686 | .说明 687 | `.push:(key \=> "value")` 添加一个新键值对。 + 688 | `.kv` 返回一个包含所有键值对的列表。 + 689 | `.keys` 返回一个包含所有键的列表。 + 690 | `.values` 返回一个包含所有值的列表。 + 691 | 我们可以通过哈希中特定值所对应的键来访问这个值。 `%hash` 692 | 693 | NOTE: 完整的哈希参考资料,请参照 https://docs.raku.org/type/Hash 694 | 695 | === 类型 696 | 前面的例子中,我们并没有指定变量中值的类型。 697 | 698 | TIP: `.WHAT` 会返回变量中值的类型。 699 | 700 | [source,perl6] 701 | ---- 702 | my $var = 'Text'; 703 | say $var; 704 | say $var.WHAT; 705 | 706 | $var = 123; 707 | say $var; 708 | say $var.WHAT; 709 | ---- 710 | 711 | 在上面的例子中,你能看到变量 `$var` 中的值先是(Str)后是(Int)。 712 | 713 | 这种编程风格被称作动态类型。动态在这里是指变量可以储存任何类型的值。 714 | 715 | 接下来试着运行下面的例子: + 716 | 注意在变量名前的 `Int`。 717 | 718 | [source,perl6] 719 | ---- 720 | my Int $var = 'Text'; 721 | say $var; 722 | say $var.WHAT; 723 | ---- 724 | 725 | 运行会失败并返回报错信息: `Type check failed in assignment to $var; expected Int but got Str 赋值给$var时类型检查失败,预期Int但是得到Str` 726 | 727 | 这是因为我们预先指定变量类型为(Int),当将(Str)赋值给它的时候就导致了运行失败。 728 | 729 | 这种编程风格被称为静态类型编程。静态在这里是指变量类型在赋值前定义并且不能在更改。 730 | 731 | Raku 属于 *渐进类型* ;它同时支持 *静态* 和 *动态* 类型。 732 | 733 | .数组和哈希同样可以为静态类型: 734 | [source,perl6] 735 | ---- 736 | my Int @array = 1,2,3; 737 | say @array; 738 | say @array.WHAT; 739 | 740 | my Str @multilingual = "Hello","Salut","Hallo","您好","안녕하세요","こんにちは"; 741 | say @multilingual; 742 | say @multilingual.WHAT; 743 | 744 | my Str %capitals = (UK => 'London', Germany => 'Berlin'); 745 | say %capitals; 746 | say %capitals.WHAT; 747 | 748 | my Int %country-codes = (UK => 44, Germany => 49); 749 | say %country-codes; 750 | say %country-codes.WHAT; 751 | 752 | my Str %student-name{Int} = (1 => 'Alex', 2 => 'Mary'); 753 | say %student-name; 754 | say %student-name.WHAT; 755 | ---- 756 | 757 | .下面是最常用的类型: 758 | 你可能永远不会用到前两种类型,他们被列出来只是为了让你知道。 759 | 760 | [cols="^.^1m,.^3m,.^2m,.^1m, options="header"] 761 | |=== 762 | 763 | | *类型* | *描述* | *例子* | *结果* 764 | 765 | | Mu | Raku 的基础类型 | | 766 | 767 | | Any | 新类和大部分内部类的默认基类 | | 768 | 769 | | Cool | 既可以被当做数值也可以作为字符串 | my Cool $var = 31; say $var.flip; say $var * 2; | 13 62 770 | 771 | | Str | 字符串 | my Str $var = "NEON"; say $var.flip; | NOEN 772 | 773 | | Int | 整数(任意精度) | 7 + 7 | 14 774 | 775 | | Rat | 有理数(有限精度)| 0.1 + 0.2 | 0.3 776 | 777 | | Bool | 布尔类型 | !True | False 778 | 779 | |=== 780 | 781 | === 自省 782 | 783 | 自省是获取对象属性信息的过程,比如获取对象的类型。 + 784 | 在前面的一个例子中我们使用 `.WHAT` 来获取变量的类型。 785 | 786 | [source,perl6] 787 | ---- 788 | my Int $var; 789 | say $var.WHAT; # (Int) 790 | my $var2; 791 | say $var2.WHAT; # (Any) 792 | $var2 = 1; 793 | say $var2.WHAT; # (Int) 794 | $var2 = "Hello"; 795 | say $var2.WHAT; # (Str) 796 | $var2 = True; 797 | say $var2.WHAT; # (Bool) 798 | $var2 = Nil; 799 | say $var2.WHAT; # (Any) 800 | ---- 801 | 802 | 变量的类型和它储存的值有关。 + 803 | 使用强声明定义的空变量,它的类型就是声明的类型 + 804 | 一个不是强声明定义的空变量,它的类型是 `(Any)` + 805 | 可以通过赋 `Nil` 给变量,来清除变量的值。 806 | 807 | === 作用域 808 | 在第一次使用变量之前,变量需要被声明。 809 | 810 | 在 Raku 中可以使用使用多种声明方式。其中 `my` 已经在上述例子中使用到。 811 | 812 | [source,perl6] 813 | my $var=1; 814 | 815 | `my` 声明指定了变量上下文作用域。 816 | 换句话说,这个变量只能在它被定义的块中被访问。 817 | 818 | Raku 中块有 `{}` 界定。 819 | 如果没有块存在,那么变量就在整个 Raku 脚本中可用。 820 | 821 | [source,perl6] 822 | ---- 823 | { 824 | my Str $var = 'Text'; 825 | say $var; # 可以使用 826 | } 827 | say $var; # 不能使用, 会报错 828 | ---- 829 | 830 | 因为一个变量只有在定义它的块中有效,所以同样的变量名可以在另一个块中使用。 831 | 832 | [source,perl6] 833 | ---- 834 | { 835 | my Str $var = 'Text'; 836 | say $var; 837 | } 838 | my Int $var = 123; 839 | say $var; 840 | ---- 841 | 842 | === 赋值 vs. 绑定 843 | 我们已经在前面的例子中看到如何将值 *赋* 给变量。 + 844 | *赋值* 通过 `=` 操作符完成。 845 | [source,perl6] 846 | ---- 847 | my Int $var = 123; 848 | say $var; 849 | ---- 850 | 851 | 我们可以改变赋给变量的值: 852 | 853 | [source,perl6] 854 | .赋值 855 | ---- 856 | my Int $var = 123; 857 | say $var; 858 | $var = 999; 859 | say $var; 860 | ---- 861 | 862 | .`输出` 863 | ---- 864 | 123 865 | 999 866 | ---- 867 | 868 | 我们不能改变 *绑定* 到变量上的值 + 869 | *绑定* 通过 `:=` 操作符实现。 870 | 871 | [source,perl6] 872 | .绑定 873 | ---- 874 | my Int $var := 123; 875 | say $var; 876 | $var = 999; 877 | say $var; 878 | ---- 879 | 880 | .`输出` 881 | ---- 882 | 123 883 | Cannot assign to an immutable value 不允许赋值到一个不可变值 884 | ---- 885 | 886 | [source,perl6] 887 | .变量也可以绑定到其他变量: 888 | ---- 889 | my $a; 890 | my $b; 891 | $b := $a; 892 | $a = 7; 893 | say $b; 894 | $b = 8; 895 | say $a; 896 | ---- 897 | 898 | .`输出` 899 | ---- 900 | 7 901 | 8 902 | ---- 903 | 904 | 就像你已经注意到的那样,绑定变量是双向的。 + 905 | `$a := $b` 和 `$b := $a` 拥有同样的效果. 906 | 907 | NOTE: 关于变量的更多信息,请参见 https://docs.raku.org/language/variables 908 | 909 | == 函数和修改器 910 | 911 | 把函数和修改器区分开来很重要。 + 912 | 函数在调用的时候不改变对象的初始状态。 + 913 | 修改器改变对象的状态。 914 | 915 | [source,perl6,linenums] 916 | .`脚本` 917 | ---- 918 | my @numbers = [7,2,4,9,11,3]; 919 | 920 | @numbers.push(99); 921 | say @numbers; #1 922 | 923 | say @numbers.sort; #2 924 | say @numbers; #3 925 | 926 | @numbers.=sort; 927 | say @numbers; #4 928 | ---- 929 | 930 | .`输出` 931 | ---- 932 | [7 2 4 9 11 3 99] #1 933 | (2 3 4 7 9 11 99) #2 934 | [7 2 4 9 11 3 99] #3 935 | [2 3 4 7 9 11 99] #4 936 | ---- 937 | 938 | .说明 939 | `.push` 是一个修改器,它会改变数组的状态。(#1) 940 | 941 | `.sort` 是一个函数,它返回排序后的数组,但是不改变原始数组。 942 | 943 | * (#2) 展示了它返回排序后的数组。 944 | 945 | * (#3) 展示了原始数组没有被修改。 946 | 947 | 为了强制一个函数作为修改器那样对原始数据进行修改,而我们可以使用 `.=` 来代替 `.` (#4) (脚本的第9行) 948 | 949 | == 循环和条件语句 950 | Raku 有多种条件和循环结构。 951 | 952 | === if 953 | 仅在条件满足(条件表达式结果为 `True`)时运行代码。 954 | 955 | [source,perl6] 956 | ---- 957 | my $age = 19; 958 | 959 | if $age > 18 { 960 | say 'Welcome' 961 | } 962 | ---- 963 | 964 | 在 Raku 中我们能倒装条件语句和待运行的代码。 + 965 | 将待运行的代码和条件语句倒装后,条件语句总是优先被执行。 966 | 967 | [source,perl6] 968 | ---- 969 | my $age = 19; 970 | 971 | say 'Welcome' if $age > 18; 972 | ---- 973 | 974 | 如果条件不满足,我们可以指定执行另一个代码块: 975 | 976 | * `else` 977 | * `elsif` 978 | 979 | [source,perl6] 980 | ---- 981 | # 为变量不同的值运行相同的代码 982 | my $座位数 = 9; 983 | 984 | if $座位数 <= 5 { 985 | say '我是小轿车' 986 | } elsif $座位数 <= 7 { 987 | say '我是七座车' 988 | } else { 989 | say '我是面包车' 990 | } 991 | ---- 992 | 993 | === unless 994 | `unless` 是否定形式的 if。 995 | 996 | 下面这些代码: 997 | 998 | [source,perl6] 999 | ---- 1000 | my $clean-shoes = False; 1001 | 1002 | if not $clean-shoes { 1003 | say 'Clean your shoes' 1004 | } 1005 | ---- 1006 | 可以改写成: 1007 | 1008 | [source,perl6] 1009 | ---- 1010 | my $clean-shoes = False; 1011 | 1012 | unless $clean-shoes { 1013 | say 'Clean your shoes' 1014 | } 1015 | ---- 1016 | 1017 | `否` 运算在 Raku 中用 `!` 或 `not` 来实现。 1018 | 1019 | `unless (条件)` 可以用来代替 `if not (条件)`。 1020 | 1021 | 需要注意 `unless` 不能配合使用 `else`。 1022 | 1023 | === with 1024 | 1025 | `with` 的用法和 `if` 相同, 不过 `with` 会检查变量是否已经定义。 + 1026 | 如果变量没有定义,那么就不会执行块中的代码。 1027 | 1028 | [source,perl6] 1029 | ---- 1030 | my Int $var=1; 1031 | 1032 | with $var { 1033 | say 'Hello' 1034 | } 1035 | ---- 1036 | 1037 | 以下代码中,如果你没有给 `$var` 赋值,那么代码块就不会被执行。 1038 | [source,perl6] 1039 | ---- 1040 | my Int $var; 1041 | 1042 | with $var { 1043 | say 'Hello' 1044 | } 1045 | ---- 1046 | 1047 | `without` 是否定版的 `with`。你可以拿 `if` 和 `unless` 的关系做类比。 1048 | 1049 | 如果第一个 `with` 条件不能满足,替代执行的代码块可以使用 `orwith` 来指定。 + 1050 | `with` 和 `orwith` 的关系可以同 `if` 与 `elsif` 的关系相比较。 1051 | 1052 | === for 1053 | 1054 | `for` 循环可以迭代多个值。 1055 | 1056 | [source,perl6] 1057 | ---- 1058 | my @array = [1,2,3]; 1059 | 1060 | for @array -> $array-item { 1061 | say $array-item * 100 1062 | } 1063 | ---- 1064 | 1065 | 需要注意到我们创建了一个循环变量 `$array-item` 用来对每个数组元素进行 `*100` 的操作。 1066 | 1067 | === given 1068 | 1069 | 在 Raku 中的 `given` 类似其他语言中的 switch,但是要更加地强大。 1070 | 1071 | [source,perl6] 1072 | ---- 1073 | my $var = 42; 1074 | 1075 | given $var { 1076 | when 0..50 { say 'Less than or equal to 50'} 1077 | when Int { say "is an Int" } 1078 | when 42 { say 42 } 1079 | default { say "huh?" } 1080 | } 1081 | ---- 1082 | 1083 | 在成功匹配后,匹配就会停止。 1084 | 1085 | 如果在匹配成功后运行的代码块中加了 `proceed`,那么就要在下一次成功匹配后才停止匹配。 1086 | 1087 | [source,perl6] 1088 | ---- 1089 | my $var = 42; 1090 | 1091 | given $var { 1092 | when 0..50 { say 'Less than or equal to 50';proceed} 1093 | when Int { say "is an Int";proceed} 1094 | when 42 { say 42 } 1095 | default { say "huh?" } 1096 | } 1097 | ---- 1098 | 1099 | === loop 1100 | 1101 | `loop` 是进行 `for` 循环的另一种方式。 + 1102 | Raku 中 C 语言家族的 `for` 循环使用 `loop` 来表示。 + 1103 | Raku 是 C 语言家族中的一员。 1104 | 1105 | [source,perl6] 1106 | ---- 1107 | loop (my $i = 0; $i < 5; $i++) { 1108 | say "The current number is $i" 1109 | } 1110 | ---- 1111 | 1112 | NOTE: 更多关于循环和条件语句的信息,参见 https://docs.raku.org/language/control 1113 | 1114 | 1115 | == I/O 1116 | 在 Raku 中,两个最常用的输入/输出的端口是终端和文件。 1117 | 1118 | === 在终端中基本的 I/O 操作 1119 | 1120 | ==== say 1121 | `say` 能输出到标准输出,而且它还会在输出的最后添加一个换行符,请见下面的示例代码: 1122 | 1123 | [source,perl6] 1124 | ---- 1125 | say 'Hello Mam.'; 1126 | say 'Hello Sir.'; 1127 | ---- 1128 | 会在两行中分别打印出来。 1129 | 1130 | ==== print 1131 | `print` 的功能和 `say` 相似,但是它不会在输出内容后添加换行符。 1132 | 1133 | 试试将 `say` 换成 `print` 比较一下它们两者的特点。 1134 | 1135 | ==== get 1136 | `get` 用来获取终端中的输入。 1137 | 1138 | [source,perl6] 1139 | ---- 1140 | my $name; 1141 | 1142 | say "Hi, what's your name?"; 1143 | $name = get; 1144 | 1145 | say "Dear $name welcome to Raku"; 1146 | ---- 1147 | 1148 | 当上面的代码运行时,终端会等你输入名字和回车键。 1149 | 然后就会向你打招呼。 1150 | 1151 | ==== prompt 1152 | `prompt` 整合了 `print` 和 `get` 的功能。 1153 | 1154 | 以上的例子可以改写成以下方式: 1155 | 1156 | [source,perl6] 1157 | ---- 1158 | my $name = prompt "Hi, what's your name? "; 1159 | 1160 | say "Dear $name welcome to Raku"; 1161 | ---- 1162 | 1163 | === 运行 Shell 命令 1164 | 有两种子例程可以用来运行 shell 命令: 1165 | 1166 | * `run` 不通过 shell 运行外部命令。 1167 | 1168 | * `shell` 通过系统 shell 来运行命令。它依赖系统平台和它的 shell。 1169 | 所有的保留字(meta chracters) 会被 shell 解释,包括管道(pipes)、重定向、环境变量替换等等。 1170 | 1171 | [source,perl6] 1172 | .如果你用的是 Linux 或 OSX 请运行以下代码: 1173 | ---- 1174 | my $name = 'Neo'; 1175 | run 'echo', "hello $name"; 1176 | shell "ls"; 1177 | ---- 1178 | 1179 | [source,perl6] 1180 | .如果你用的是 Windows 请运行下面这段代码: 1181 | ---- 1182 | shell "dir"; 1183 | ---- 1184 | `echo` 和 `ls` 是常用的 Linux shell 命令 + 1185 | `echo` 打印文本到终端(和 Raku 中的 `print` 相同 + 1186 | `ls` 列出当前目录下所有的文件和文件夹。 1187 | 1188 | `dir` 在 Windows 中和 `ls` 的功能相同。 1189 | 1190 | 1191 | === File I/O 1192 | ==== slurp 1193 | `slurp` 用来从文件中读入数据。 1194 | 1195 | 建立一个文本文件包含以下内容: 1196 | 1197 | .datafile.txt 1198 | ---- 1199 | John 9 1200 | Johnnie 7 1201 | Jane 8 1202 | Joanna 7 1203 | ---- 1204 | [source,perl6] 1205 | ---- 1206 | my $data = slurp "datafile.txt"; 1207 | say $data; 1208 | ---- 1209 | 1210 | ==== spurt 1211 | `spurt` 用来向文件写入数据。 1212 | 1213 | [source,perl6] 1214 | ---- 1215 | my $newdata = "New scores: 1216 | Paul 10 1217 | Paulie 9 1218 | Paulo 11"; 1219 | 1220 | spurt "newdatafile.txt", $newdata; 1221 | ---- 1222 | 1223 | 在运行上述代码后,一个存储有 New score 的文件 _newdatafile.txt_ 会被创建。 1224 | 1225 | === 文件和文件目录操作 1226 | 在先前的例子中,Raku 能不使用 shell 命令的情况下列出一个目录下所有的内容。 1227 | 1228 | [source,perl6] 1229 | ---- 1230 | say dir; # 列出当前目录下的文件和文件夹 1231 | say dir "/Documents"; # 列出特定目录下的文件和文件夹 1232 | ---- 1233 | 1234 | 另外你还能创建和删除目录。 1235 | 1236 | [source,perl6] 1237 | ---- 1238 | mkdir "newfolder"; 1239 | rmdir "newfolder"; 1240 | ---- 1241 | 1242 | `mkdir` 创建一个新的目录。 + 1243 | `rmdir` 删除一个空目录,如果目录不为空则返回 error。 1244 | 1245 | 你还可以检查特定的路径是否存在: 1246 | 1247 | 在你要运行以下脚本的目录中,建立一个叫 `folder123` 的空文件夹和一个文件 `script123.raku` 1248 | 1249 | [source,perl6] 1250 | ---- 1251 | say "script123.raku".IO.e; 1252 | say "folder123".IO.e; 1253 | 1254 | say "script123.raku".IO.d; 1255 | say "folder123".IO.d; 1256 | 1257 | say "script123.raku".IO.f; 1258 | say "folder123".IO.f; 1259 | ---- 1260 | 1261 | `IO.e` 检查文件夹或文件是否存在。 + 1262 | `IO.f` 检查路径所指的是否为文件。 + 1263 | `IO.d` 检查路径所指的是否为文件夹。 1264 | 1265 | WARNING: Windows 下使用 `/` 或 `\\` 来定义目录 + 1266 | `C:\\rakudo\\bin` + 1267 | `C:/rakudo/bin` + 1268 | 1269 | NOTE: 更多关于 I/O,详见 https://docs.raku.org/type/IO 1270 | 1271 | == 子例程 1272 | === 定义 1273 | *子例程* (也叫 *subs* 或 *functions* ) 是一种功能集的打包。 + 1274 | 1275 | 定义子例程时以关键字 `sub` 起始。在定义之后你能通过子例程名来进行调用。 + 1276 | 让我们来看下面的例子: 1277 | 1278 | [source,perl6] 1279 | ---- 1280 | sub alien-greeting { 1281 | say "Hello earthlings"; 1282 | } 1283 | 1284 | alien-greeting; 1285 | ---- 1286 | 1287 | 上面的例子给我们展示了一个不需要输入参数的子例程。 1288 | 1289 | === Signature 函数签名 1290 | 很多子例程需要多个输入才能运行。这些输入由 *参数* 提供。 1291 | 一个子例程可以不定义或定义多个 *参数*。 1292 | 子例程所定义的参数之个数与类型称为它的 *函数签名*。 1293 | 1294 | 下面的子例程接收一个字符串参数。 1295 | 1296 | [source,perl6] 1297 | ---- 1298 | sub say-hello (Str $name) { 1299 | say "Hello " ~ $name ~ "!!!!" 1300 | } 1301 | say-hello "Paul"; 1302 | say-hello "Paula"; 1303 | ---- 1304 | 1305 | === 多重分派 1306 | 我们可以定义多个有相同命名但有不同函数签名的的子例程。 1307 | 当这样的子例程被调用的时候,运行环境会根据提供的参数之数量和类型判断那个版本的同名子例程来运行。 1308 | 这种子例程和普通的子例程的定义方法是一样的,不过我们需要使用 `multi` 来代替原先的 `sub`。 1309 | 1310 | [source,perl6] 1311 | ---- 1312 | multi greet($name) { 1313 | say "Good morning $name"; 1314 | } 1315 | multi greet($name, $title) { 1316 | say "Good morning $title $name"; 1317 | } 1318 | 1319 | greet "Johnnie"; 1320 | greet "Laura","Mrs."; 1321 | ---- 1322 | 1323 | === 默认参数和可选参数 1324 | 如果一个子例程被定义需要一个参数,但是我们调用它的时候没有提供所需的参数,那么这个子例程就不能运行。 1325 | 1326 | 不过 Raku 提供了: 1327 | 1328 | * 可选参数 1329 | * 默认参数 1330 | 1331 | 可选参数在定义的时候需要在参数名后加 `?`。 1332 | 1333 | [source,perl6] 1334 | ---- 1335 | sub say-hello($name?) { 1336 | with $name { say "Hello " ~ $name } 1337 | else { say "Hello Human" } 1338 | } 1339 | say-hello; 1340 | say-hello("Laura"); 1341 | ---- 1342 | 1343 | 如果使用者没有提供参数,那么它就默认使用预先给定的值。 + 1344 | 上面是通过在子例程中给参数赋值的方式实现的。 1345 | 1346 | [source,perl6] 1347 | ---- 1348 | sub say-hello($name="Matt") { 1349 | say "Hello " ~ $name; 1350 | } 1351 | say-hello; 1352 | say-hello("Laura"); 1353 | ---- 1354 | 1355 | === 返回值 1356 | 我们现在为止看到的子例程都在终端中打印一些文字。 1357 | 1358 | 一些时候我们需要让子例程 *返回* 值让我们能在我们的程序中能重复使用。 1359 | 1360 | 在一般的情景下,子例程代码的最后一行被默认为返回值。 1361 | [source,perl6] 1362 | .隐式返回 1363 | ---- 1364 | sub squared ($x) { 1365 | $x ** 2; 1366 | } 1367 | say "7 squared is equal to " ~ squared(7); 1368 | ---- 1369 | 1370 | 一旦我们的代码变多,清楚地指明我们需要返回的变量是很有用的。 1371 | 可以使用 `return` 关键字来指明返回变量。 1372 | [source,perl6] 1373 | .显式返回 1374 | ---- 1375 | sub squared ($x) { 1376 | return $x ** 2; 1377 | } 1378 | say "7 squared is equal to " ~ squared(7); 1379 | ---- 1380 | ==== 限制返回值 1381 | 在之前的例子中,我们对将子例程的参数限定为特定类型。 1382 | 我们可以同样地限定返回值的类型。 1383 | 1384 | 为了将返回值限定为特定类型,我们可以使用 `returns` 特征 (trait) 或在函数签名中使用箭标。 1385 | 1386 | [source,perl6] 1387 | .使用 rerurns 特征 1388 | ---- 1389 | sub squared ($x) returns Int { 1390 | return $x ** 2; 1391 | } 1392 | say "1.2 squared is equal to " ~ squared(1.2); 1393 | ---- 1394 | 1395 | [source,perl6] 1396 | .使用箭头 1397 | ---- 1398 | sub squared ($x --> Int) { 1399 | return $x ** 2; 1400 | } 1401 | say "1.2 squared is equal to " ~ squared(1.2); 1402 | ---- 1403 | 如果子例程不能提供符合类型要求的返回值,程序就会报错。 1404 | 1405 | ---- 1406 | Type check failed for return value; expected Int but got Rat (1.44) 返回值类型检查失败,预期Int但是得到Rat(1.44) 1407 | ---- 1408 | 1409 | [TIP] 1410 | ==== 1411 | 类型限制不仅可以控制返回值的类型,还可以控制返回值的定义状态。 1412 | 1413 | 之前的例子中,我们指定了返回值必须为 `Int`。 + 1414 | 还可以进一步严格指定返回值 `Int` 是已定义(defined)或未定义(undefined)的: + 1415 | `-\-> Int:D` 和 `-\-> Int:U` 1416 | 1417 | 使用类型限制是一个好的习惯。 + 1418 | 以下是将前面的例子修改后的结果,其中使用 `:D` 强制返回已定义的 `Int`。 1419 | 1420 | [source,perl6] 1421 | ---- 1422 | sub squared ($x --> Int:D) { 1423 | return $x ** 2; 1424 | } 1425 | say "1.2 squared is equal to " ~ squared(1.2); 1426 | ---- 1427 | ==== 1428 | 1429 | NOTE: 关于更多子例程和函数的资料,详见 https://docs.raku.org/language/functions 1430 | 1431 | 1432 | == 函数式编程 1433 | 1434 | 在本章中,我们将看看一些有利于函数式编程的功能。 1435 | 1436 | === 函数是一等公民 1437 | 1438 | 函数/子例程是一等公民: 1439 | 1440 | - 它们能作为参数传递 1441 | - 它们能从另外一个函数中返回 1442 | - 它们能被赋值给变量 1443 | 1444 | `map` 函数是用来说明这个概念的极好例子。 + 1445 | `map` 是 *高阶函数*, 它接收另外一个函数作为参数。 1446 | 1447 | [source,perl6] 1448 | .脚本 1449 | ---- 1450 | my @array = <1 2 3 4 5>; 1451 | sub squared($x) { 1452 | $x ** 2 1453 | } 1454 | say map(&squared, @array); 1455 | ---- 1456 | 1457 | .输出 1458 | 1459 | ---- 1460 | (1 4 9 16 25) 1461 | ---- 1462 | 1463 | .解释 1464 | 1465 | 我们定义了一个叫做 `squared` 的子例程, 它接收一个数字并返回该数字的二次幂。 + 1466 | 下一步, 我们使用 `map` 这个高阶函数并传递给它两个参数, 一个子例程和一个数组。 + 1467 | 结果是所有数组元素的平方组成的列表。 1468 | 1469 | 注意当传递子例程作为参数时, 我们需要在子例程的名字前添加一个 `&` 符号。 1470 | 1471 | === 匿名函数 1472 | *匿名函数* 也叫做 *拉姆达*(lambda)。 + 1473 | 匿名函数没有绑定到标识符(匿名函数没有名字)。 1474 | 1475 | 让我们使用匿名函数重写 `map` 那个例子。 1476 | [source,perl6] 1477 | ---- 1478 | my @array = <1 2 3 4 5>; 1479 | say map(-> $x {$x ** 2}, @array); 1480 | ---- 1481 | 注意我们没有声明子例程并把它作为参数传递给 `map`, 而是在里面直接定义了匿名函数。 + 1482 | 匿名函数 `\-> $x {$x ** 2}` 没有句柄并且不能被调用。 1483 | 1484 | 按照 Raku 的说法我们把这个标记叫做 *pointy block*。 1485 | 1486 | [source,perl6] 1487 | .pointy block 也能用于把函数赋值给变量: 1488 | ---- 1489 | my $squared = -> $x { 1490 | $x ** 2 1491 | } 1492 | say $squared(9); 1493 | ---- 1494 | 1495 | === 链式调用 1496 | 1497 | 在 Raku中, 方法可以链接起来, 你不再需要把一个方法的结果作为参数传递给另外一个方法了。 1498 | 1499 | 我们假设你有一个数组。你被要求返回该数组的唯一值, 并且按从大到小的顺序排序。 1500 | 1501 | 下面是没有使用链式调用的代码: 1502 | [source,perl6] 1503 | ---- 1504 | my @array = <7 8 9 0 1 2 4 3 5 6 7 8 9 >; 1505 | my @final-array = reverse(sort(unique(@array))); 1506 | say @final-array; 1507 | ---- 1508 | 首先我们在 `@array` 上调用 `unique` 函数, 然后我们把它的结果作为参数传递给 `sort` 函数, 再然后我们把结果传递给 `reverse` 函数。 1509 | 1510 | 和上面的例子相比, Raku 允许链式方法。 + 1511 | 上面的例子可以像下面这样写, 利用 *方法链* 的优点: 1512 | 1513 | [source,perl6] 1514 | ---- 1515 | my @array = <7 8 9 0 1 2 4 3 5 6 7 8 9 >; 1516 | my @final-array = @array.unique.sort.reverse; 1517 | say @final-array; 1518 | ---- 1519 | 1520 | 你已经看到链式方法看起来有多 _清爽_ 啦。 1521 | 1522 | === Feed 操作符 1523 | *feed 操作符*, 在有些函数式编程语言中也叫 _管道_, 然而它是链式方法的一个更好的可视化产出。 1524 | [source,perl6] 1525 | .正向流(Forward Feed) 1526 | ---- 1527 | my @array = <7 8 9 0 1 2 4 3 5 6>; 1528 | @array ==> unique() 1529 | ==> sort() 1530 | ==> reverse() 1531 | ==> my @final-array; 1532 | say @final-array; 1533 | ---- 1534 | 1535 | .解释 1536 | ---- 1537 | 从 `@array` 开始 然后 返回一个唯一元素的列表 1538 | 然后 排序它 1539 | 然后 反转它 1540 | 然后 把结果保存到 @final-array 中 1541 | ---- 1542 | 就像你看到的那样, 方法的流向是自上而下的。 1543 | 1544 | 1545 | [source,perl6] 1546 | .反向流(Backward Feed) 1547 | ---- 1548 | my @array = <7 8 9 0 1 2 4 3 5 6>; 1549 | my @final-array-v2 <== reverse() 1550 | <== sort() 1551 | <== unique() 1552 | <== @array; 1553 | say @final-array-v2; 1554 | ---- 1555 | 1556 | .解释 1557 | 1558 | 正向流就像反向流一样, 但是是以反转的顺序写的。 + 1559 | 1560 | 方法的流动方向是自下而上。 1561 | 1562 | === Hyper 操作符 1563 | *hyper 操作符* `>>.` 对列表的所有元素调用同一方法,返回其结果的列表。 1564 | [source,perl6] 1565 | ---- 1566 | my @array = <0 1 2 3 4 5 6 7 8 9 10>; 1567 | sub is-even($var) { $var %% 2 }; 1568 | 1569 | say @array>>.is-prime; 1570 | say @array>>.&is-even; 1571 | ---- 1572 | 1573 | 我们能通过使用 hyper 操作符,调用 Raku 中已经定义过的方法。例如 `is-prime` 告诉我们一个数字是否是质数。 + 1574 | 1575 | 此外我们能定义新的子例程并使用 hyper 操作符调用它们。但是这时我们必须在方法的名字前面加上 `&` 符号。例如 `&is-even`。 1576 | 1577 | 这很实用,因为我们不必写 `for` 循环就可以迭代每个值。 1578 | 1579 | WARNING: Raku 会保证结果的顺序与原始值的顺序相同。 + 1580 | 但是 *不能保证* Raku 会真正地在同一个顺序或在同一个线程中调用该方法。 + 1581 | 因此,请注意具有副作用的方法,例如 `say` 或 `print`。 1582 | 1583 | === Junction 1584 | *junction* 是值的逻辑叠加。 1585 | 1586 | 在下面的例子中 `1|2|3` 是一个junction。 1587 | [source,perl6] 1588 | ---- 1589 | my $var = 2; 1590 | if $var == 1|2|3 { 1591 | say "The variable is 1 or 2 or 3" 1592 | } 1593 | ---- 1594 | junction 的使用常常触发 **自动线程化**; 每个 junction 元素都执行该操作, 并且所有的结果被组合到一个新的 junction 中并返回。 1595 | 1596 | === 惰性列表 1597 | *惰性列表* 是被惰性求值的列表。 + 1598 | 惰性求值延迟表达式的计算直到需要时, 并把结果存储到查询表中以避免重复计算。 1599 | 1600 | 惰性列表的优点包括: 1601 | 1602 | * 通过避免不必要的计算带来的性能提升 1603 | 1604 | * 构建潜在的无限数据结构的能力 1605 | 1606 | * 定义控制流的能力 1607 | 1608 | 我们使用中缀操作符 `\...` 来创建惰性列表。 + 1609 | 惰性列表具有 *初始元素*, *生成器* 和 *结束点*。 1610 | 1611 | [source,perl6] 1612 | . 简单惰性列表 1613 | ---- 1614 | my $lazylist = (1 ... 10); 1615 | say $lazylist; 1616 | ---- 1617 | 1618 | 初始元素为 1 而结束点为 10。因为没有定义生成器所以默认的生成器为后继生成器(+1)。 + 1619 | 换句话说, 这个惰性列表可能返回(如果需要的话)下面的元素 (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)。 1620 | 1621 | [source,perl6] 1622 | . 无穷惰性列表 1623 | ---- 1624 | my $lazylist = (1 ... Inf); 1625 | say $lazylist; 1626 | ---- 1627 | 1628 | 该列表可能返回(如果需要的话) 1 到无穷大之间的任何整数, 换句话说, 可以返回任何整数。 1629 | 1630 | [source,perl6] 1631 | .使用推断生成器创建惰性列表 1632 | ---- 1633 | my $lazylist = (0,2 ... 10); 1634 | say $lazylist; 1635 | ---- 1636 | 1637 | 初始的元素是 0 和 2 而结束点是 10。虽然没有定义生成器, 但是使用了初始元素, Raku 会把生成器推断为 (+2)。 + 1638 | 1639 | 这个惰性列表可能返回(如果需要的话)下面的元素 (0, 2, 4, 6, 8, 10)。 1640 | [source,perl6] 1641 | .使用定义的生成器创建惰性列表 1642 | ---- 1643 | my $lazylist = (0, { $_ + 3 } ... 12); 1644 | say $lazylist; 1645 | ---- 1646 | 1647 | 在这个例子中, 我们在闭合 `{ }` 中显式地定义了一个生成器。 + 1648 | 1649 | 这个惰性列表可能返回(如果需要的话)下面的元素 (0, 3, 6, 9, 12)。 1650 | 1651 | [WARNING] 1652 | ==== 1653 | 当使用显式的生成器时, 结束点必须是生成器能返回的一个值。 + 1654 | 1655 | 如果在上面的例子中我们使用的结束点是 10 而非 12, 那么生成器就不会停止。生成器会 _跳过_ 那个结束点。 1656 | 1657 | 你可以使用 `0 \...^ * > 10` 代替 `0 \... 10`。 + 1658 | 你可以把它读作: 从 0 直到第一个大于 10 的值(不包含该值)。 1659 | [source,perl6] 1660 | .这不会使生成器停止 1661 | ---- 1662 | my $lazylist = (0, { $_ + 3 } ... 10); 1663 | say $lazylist; 1664 | ---- 1665 | 1666 | [source,perl6] 1667 | .这会使生成器停止 1668 | ---- 1669 | my $lazylist = (0, { $_ + 3 } ...^ * > 10); 1670 | say $lazylist; 1671 | ---- 1672 | ==== 1673 | 1674 | === 闭包 1675 | 1676 | 在 Raku 中所有的代码对象都是闭包, 这意味着它们能从外部作用域(outer scope)引用词法变量(lexical variables)。 1677 | 1678 | [source,perl6] 1679 | ---- 1680 | sub generate-greeting { 1681 | my $name = "John Doe"; 1682 | sub greeting { 1683 | say "Good Morning $name"; 1684 | }; 1685 | return &greeting; 1686 | } 1687 | my $generated = generate-greeting; 1688 | $generated(); 1689 | ---- 1690 | 1691 | 如果你运行上面的代码,它将在终端上显示 `Good Morning John Doe`。 + 1692 | 虽然结果相当简单,但这个例子有趣的是,`greeting` 内部子程序在执行之前是从外部子例程中返回的。 1693 | 1694 | `$generated` 已经变成了 *闭包*。 1695 | 1696 | *闭包* 是一种特殊类型的对象,它结合了两个东西: 1697 | 1698 | * 子例程 1699 | 1700 | * 创建该子例程的环境。 1701 | 1702 | 该环境由创建闭包时在作用域内的任何局部变量组成。 1703 | 在这种情况下,`$generated` 是一个闭包,它包含在创建闭包时存在的 `greeting` 子例程和 `John Doe` 字符串。 1704 | 1705 | 让我们来看一个更有趣的例子。 1706 | [source,perl6] 1707 | ---- 1708 | sub greeting-generator($period) { 1709 | return sub ($name) { 1710 | return "Good $period $name" 1711 | } 1712 | } 1713 | my $morning = greeting-generator("Morning"); 1714 | my $evening = greeting-generator("Evening"); 1715 | 1716 | say $morning("John"); 1717 | say $evening("Jane"); 1718 | ---- 1719 | 在这个例子中,我们定义了一个子例程 `greeting-generator($period)`,它接受单个参数 `$period` 并返回一个新的子例程。新的子例程接受单个参数 `$name` 并返回构造好的问候语。 1720 | 1721 | 基本上,`greeting-generator` 是一个子例程工厂。在这个例子中,我们使用了 `greeting-generator` 来创建两个新的子例程,一个说 `Good Morning` ,一个说 `Good Evening`。 1722 | 1723 | `$morning` 和 `$evening` 都是闭包。它们共享相同的子例程主体定义,但存储不同的环境。 + 1724 | 在 `$morning` 的环境中 `$period` 是 `Morning`。在 `$evening` 的环境中 `$period` 是 `Evening`。 1725 | 1726 | == 类和对象 1727 | 1728 | 在上一章中我们学习了 Raku 中函数式编程的便利性。 + 1729 | 在这一章中我们将看看 Raku 中的面向对象编程。 1730 | 1731 | === 介绍 1732 | _面向对象_ 编程是当今广泛使用的范式之一。 + 1733 | *对象* 是一组绑定在一起的变量和子例程。 + 1734 | 1735 | 其中的变量叫做 *属性*, 而子例程被叫做 *方法*。 + 1736 | 属性定义对象的 *状态*, 而方法定义对象的 *行为*。 1737 | 1738 | *类* 是创建 *对象* 的模板。 1739 | 1740 | 为了理解它们之间的关系, 考虑下面的例子: 1741 | 1742 | |=== 1743 | 1744 | | 房间里有 4 个人 | *对象* => 4 people 1745 | | 这 4 个人是人类 | *类* => Human 1746 | | 它们有不同的名字,年纪,性别和国籍 | *属性* => name,age,sex,nationality 1747 | 1748 | |=== 1749 | 1750 | 按 _面向对象_ 的说法, 对象是类的 *实例*。 1751 | 1752 | 考虑下面的脚本: 1753 | [source,perl6] 1754 | ---- 1755 | class Human { 1756 | has $.name; 1757 | has $.age; 1758 | has $.sex; 1759 | has $.nationality; 1760 | } 1761 | 1762 | my $john = Human.new(name => 'John', age => 23, sex => 'M', nationality => 'American'); 1763 | say $john; 1764 | ---- 1765 | 1766 | `class` 关键字用于定义类。 + 1767 | `has` 关键字用于定义类的属性。 + 1768 | `.new` 方法被称之为 *构造函数*。它创建了对象作为类的实例。 1769 | 1770 | 在上面的例子中, 新的变量 `$john` 保存了由 `Human.new()` 所定义的新 "Human" 实例。 + 1771 | 传递给 `.new()` 方法的参数用于设置底层对象的属性。 1772 | 类可以使用 `my` 来声明一个 _本地作用域_: 1773 | [source,perl6] 1774 | ---- 1775 | my class Human { 1776 | 1777 | } 1778 | ---- 1779 | 1780 | === 封装 1781 | 封装是一个面向对象的概念, 它把一组数据和方法捆绑在一块。 + 1782 | 对象中的数据(属性)应该是 *私有的*, 换句话说, 只能从对象内部访问它。 + 1783 | 为了从对象外部访问对象的属性, 我们使用叫做 *存取器* 的方法。 1784 | 1785 | 下面两个脚本拥有同样的结果。 1786 | 1787 | .直接访问变量: 1788 | [source,perl6] 1789 | ---- 1790 | my $var = 7; 1791 | say $var; 1792 | ---- 1793 | 1794 | .封装: 1795 | [source,perl6] 1796 | ---- 1797 | my $var = 7; 1798 | sub sayvar { 1799 | $var; 1800 | } 1801 | say sayvar; 1802 | ---- 1803 | 1804 | `sayvar` 是一个存取器。它让我们通过不直接访问这个变量来访问这个变量。 1805 | 在 Raku 中使用 *twigil* 使得封装很便利。 + 1806 | twigil 是次级的 _魔符(sigil)_ ,用于魔符和属性名之间。 + 1807 | 有两种 twigil 可用于类中: 1808 | 1809 | * `!` 用于显式地声明属性是私有的 1810 | * `.` 用于为属性自动生成存取器 1811 | 1812 | 默认地, 所有的属性都是私有的, 但是总是用 `!` twigil 是一个好习惯。 1813 | 1814 | 因此, 我们应该把上面的类重写成下面这样: 1815 | [source,perl6] 1816 | ---- 1817 | class Human { 1818 | has $!name; 1819 | has $!age; 1820 | has $!sex; 1821 | has $!nationality; 1822 | } 1823 | 1824 | my $john = Human.new(name => 'John', age => 23, sex => 'M', nationality => 'American'); 1825 | say $john; 1826 | ---- 1827 | 给脚本追加这样的的语句: `say $john.age`; + 1828 | 它会返回这样的错误: `Method 'age' not found for invocant of class 'Human' 在类'Human'中没有找到请求的方法'age'`。 + 1829 | 原因是 `$!age` 是私有的并且只能用于对象内部。 尝试在对象外部访问它会返回一个错误。 1830 | 1831 | 现在用 `has $.age` 代替 `$!age` 并看看 `say $john.age;` 的结果是什么。 1832 | 1833 | 1834 | === 具名参数 vs. 位置参数 1835 | 在 Raku 中, 所有的类继承了一个默认的 `.new` 构造函数。 + 1836 | 通过为他提供参数, 它能用于创建对象。 + 1837 | 只能提供 *具名参数* 给默认的构造函数。 + 1838 | 1839 | 如果你考虑上面的例子, 你会看到所有提供给 `.new` 方法的参数都是按名字定义的: 1840 | 1841 | * name \=> 'John' 1842 | 1843 | * age \=> 23 1844 | 1845 | 假如我不想在每次创建新对象的时候为每个属性提供一个名字呢? + 1846 | 那么我需要创建另外一个接收 *位置参数* 的构造函数。 1847 | 1848 | [source,perl6] 1849 | ---- 1850 | class Human { 1851 | has $.name; 1852 | has $.age; 1853 | has $.sex; 1854 | has $.nationality; 1855 | 1856 | # 重写默认构造函数的新构造函数 1857 | method new ($name, $age, $sex, $nationality) { 1858 | self.bless(:$name, :$age, :$sex, :$nationality); 1859 | } 1860 | } 1861 | 1862 | my $john = Human.new('John', 23, 'M', 'American'); 1863 | say $john; 1864 | ---- 1865 | 1866 | === 方法 1867 | 1868 | ==== 介绍 1869 | 1870 | 方法是对象的 _子例程_。 + 1871 | 像子例程一样, 方法是一种打包一组功能的手段, 它们接收 *参数*, 拥有 *签名* 并可以被定义为 *multi*。 1872 | 1873 | 方法是使用关键字 `method` 来定义的。 + 1874 | 正常情况下, 方法被要求在对象的属性身上执行一些动作。这强制了封装的概念。对象的属性只能在对象里面使用方法来操作。在对象外面, 只能和对象的方法交互, 并且不能访问它的属性。 1875 | 1876 | [source,perl6] 1877 | ---- 1878 | class Human { 1879 | has $.name; 1880 | has $.age; 1881 | has $.sex; 1882 | has $.nationality; 1883 | has $.eligible; 1884 | method assess-eligibility { 1885 | if self.age < 21 { 1886 | $!eligible = 'No' 1887 | } else { 1888 | $!eligible = 'Yes' 1889 | } 1890 | } 1891 | } 1892 | 1893 | my $john = Human.new(name => 'John', age => 23, sex => 'M', nationality => 'American'); 1894 | $john.assess-eligibility; 1895 | say $john.eligible; 1896 | ---- 1897 | 1898 | 一旦方法定义在类中, 它们就能在对象身上使用 _点记号_ 来调用: + 1899 | _object_ *.* _method_ 或像上面的例子那样: `$john.assess-eligibility`。 1900 | 1901 | 在方法的定义中, 如果我们需要引用对象本身以调用另一个方法, 则使用 `self` 关键字。 + 1902 | 1903 | 在方法的定义中, 如果我们需要引用属性, 则使用 `!` , 即使属性是使用 `.` 定义的。 + 1904 | 理由是 `.` twigil 做的就是使用 `!` 声明一个属性并自动创建存取器。 1905 | 1906 | 在上面的例子中, `if self.age < 21` 和 `if $!age < 21` 会有同样的效果, 尽管它们从技术上来讲是不同的: 1907 | 1908 | * `self.age` 调用了 `.age` 方法(存取器) + 1909 | 二选一, 还能写成 `$.age` 1910 | * `$!age` 是直接调用那个变量 1911 | 1912 | ==== 私有方法 1913 | 1914 | 正常的方法能从类的外面在对象身上调用。 1915 | 1916 | *私有方法* 是只能从类的内部调用的方法。 + 1917 | 一个可能的使用情况是一个方法调用另外一个执行特定动作的方法。 1918 | 连接外部世界的方法是公共的而被引用的那个方法应该保持私有。我们不想让用户直接调用它, 所以我们把它声明为私有的。 1919 | 1920 | 私有方法的声明需要在方法的名字前使用 `!` twigil。 + 1921 | 私有方法是使用 `!` 而非 `.` 调用的。 1922 | 1923 | [source,perl6] 1924 | ---- 1925 | method !iamprivate { 1926 | # code goes in here 1927 | } 1928 | 1929 | method iampublic { 1930 | self!iamprivate; 1931 | # do additional things 1932 | } 1933 | ---- 1934 | 1935 | === 类属性 1936 | 1937 | *类属性* 是属于类自身而非类的对象的属性。 + 1938 | 它们能在定义期间初始化。 + 1939 | 类属性是使用 `my` 关键字而非 `has` 关键字声明的。 + 1940 | 它们是在类自己身上而非它的对象身上调用的。 1941 | 1942 | [source,perl6] 1943 | ---- 1944 | class Human { 1945 | has $.name; 1946 | my $.counter = 0; 1947 | method new($name) { 1948 | Human.counter++; 1949 | self.bless(:$name); 1950 | } 1951 | } 1952 | my $a = Human.new('a'); 1953 | my $b = Human.new('b'); 1954 | 1955 | say Human.counter; 1956 | ---- 1957 | 1958 | === 访问类型 1959 | 1960 | 到现在为止我们看到的所以例子都使用存取器来从对象属性中获取信息。 1961 | 1962 | 假如我们需要修改属性的值呢? + 1963 | 1964 | 我们需要使用下面的 `is rw` 关键字把它标记为 _read/write_。 1965 | [source,perl6] 1966 | ---- 1967 | class Human { 1968 | has $.name; 1969 | has $.age is rw; 1970 | } 1971 | my $john = Human.new(name => 'John', age => 21); 1972 | say $john.age; 1973 | 1974 | $john.age = 23; 1975 | say $john.age; 1976 | ---- 1977 | 1978 | 默认地, 所有属性都声明为 _只读_, 但是你可以显式地使用 `is readonly` 来声明。 1979 | 1980 | === 继承 1981 | ==== 介绍 1982 | 1983 | *继承* 是面向对象编程的另一个概念。 1984 | 1985 | 当定义类的时候, 很快我们会意思到很多属性/方法在很多类中是共有的。 + 1986 | 我们应该重复代码吗? + 1987 | 不! 我们应该使用 *继承*。 1988 | 1989 | 假设我们想定义两个类, 一个类是 Human, 一个类是 Employees。 + 1990 | Human 拥有两个属性: name 和 age。 + 1991 | Employees 拥有 4 个属性: name, age, company 和 salary。 1992 | 1993 | 尝试按照下面的方式定义类: 1994 | [source,perl6] 1995 | ---- 1996 | class Human { 1997 | has $.name; 1998 | has $.age; 1999 | } 2000 | 2001 | class Employee { 2002 | has $.name; 2003 | has $.age; 2004 | has $.company; 2005 | has $.salary; 2006 | } 2007 | ---- 2008 | 2009 | 虽然上面的代码技术上是正确的, 但是概念上差。 2010 | 2011 | 更好的写法是下面这样: 2012 | [source,perl6] 2013 | ---- 2014 | class Human { 2015 | has $.name; 2016 | has $.age; 2017 | } 2018 | class Employee is Human { 2019 | has $.company; 2020 | has $.salary; 2021 | } 2022 | ---- 2023 | 2024 | `is` 关键字定义了继承。 + 2025 | 按面向对象的说法, Employee 是 Human 的 *孩子*, 而 Human 是 Employee 的 *父亲*。 2026 | 2027 | 所有的子类继承了父类的属性和方法, 所以没有必要重新定义它们。 2028 | 2029 | ==== 重写 2030 | 2031 | 类从它们的父类中继承所有的属性和方法。 + 2032 | 有些情况下, 我们需要让子类中的方法表现得和继承的方法不一样。 + 2033 | 为了做到这, 我们在子类中重新定义方法。 + 2034 | 这个概念就叫做 *重写*。 2035 | 2036 | 在下面的例子中, `introduce-yourself` 方法被 Employee 类继承。 2037 | 2038 | [source,perl6] 2039 | ---- 2040 | class Human { 2041 | has $.name; 2042 | has $.age; 2043 | method introduce-yourself { 2044 | say 'Hi 我是人类, 我的名字是 ' ~ self.name; 2045 | } 2046 | } 2047 | 2048 | class Employee is Human { 2049 | has $.company; 2050 | has $.salary; 2051 | } 2052 | 2053 | my $john = Human.new(name => 'John', age => 23,); 2054 | my $jane = Employee.new(name => 'Jane', age => 25, company => 'Acme', salary => 4000); 2055 | 2056 | $john.introduce-yourself; 2057 | $jane.introduce-yourself; 2058 | ---- 2059 | 2060 | 重写工作如下: 2061 | 2062 | [source,perl6] 2063 | ---- 2064 | class Human { 2065 | has $.name; 2066 | has $.age; 2067 | method introduce-yourself { 2068 | say 'Hi 我是人类, 我的名字是 ' ~ self.name; 2069 | } 2070 | } 2071 | 2072 | class Employee is Human { 2073 | has $.company; 2074 | has $.salary; 2075 | method introduce-yourself { 2076 | say 'Hi 我是一名员工, 我的名字是 ' ~ self.name ~ ' 我工作在: ' ~ self.company; 2077 | } 2078 | } 2079 | 2080 | my $john = Human.new(name =>'John',age => 23,); 2081 | my $jane = Employee.new(name =>'Jane',age => 25,company => 'Acme',salary => 4000); 2082 | 2083 | $john.introduce-yourself; 2084 | $jane.introduce-yourself; 2085 | ---- 2086 | 2087 | 根据对象所属的类, 会调用正确的方法。 2088 | 2089 | ==== Submethod 2090 | 2091 | *submethod* 是一种子类继承不到的方法。 + 2092 | 它们只能从所声明的类中访问。 + 2093 | 它们使用 `submethod` 关键字定义。 2094 | 2095 | === 多重继承 2096 | 2097 | 在 Raku 中允许多重继承。一个类可以继承自多个其它的类。 2098 | 2099 | [source,perl6] 2100 | ---- 2101 | class bar-chart { 2102 | has Int @.bar-values; 2103 | method plot { 2104 | say @.bar-values; 2105 | } 2106 | } 2107 | 2108 | class line-chart { 2109 | has Int @.line-values; 2110 | method plot { 2111 | say @.line-values; 2112 | } 2113 | } 2114 | 2115 | class combo-chart is bar-chart is line-chart { 2116 | } 2117 | 2118 | my $actual-sales = bar-chart.new(bar-values => [10,9,11,8,7,10]); 2119 | my $forecast-sales = line-chart.new(line-values => [9,8,10,7,6,9]); 2120 | 2121 | my $actual-vs-forecast = combo-chart.new(bar-values => [10,9,11,8,7,10], 2122 | line-values => [9,8,10,7,6,9]); 2123 | say "实际的销售: "; 2124 | $actual-sales.plot; 2125 | say "预测的销售: "; 2126 | $forecast-sales.plot; 2127 | say "实际 vs 预测:"; 2128 | $actual-vs-forecast.plot; 2129 | ---- 2130 | 2131 | .`输出` 2132 | 2133 | ---- 2134 | 实际的销售: 2135 | [10 9 11 8 7 10] 2136 | 预测的销售: 2137 | [9 8 10 7 6 9] 2138 | 实际 vs 预测: 2139 | [10 9 11 8 7 10] 2140 | ---- 2141 | 2142 | .解释 2143 | 2144 | `combo-chart` 类应该能持有两个序列, 一个是绘制条形图的实际值, 另一个是绘制折线图的预测值。 + 2145 | 这就是我们为什么把它定义为 `line-chart` 和 `bar-chart` 的孩子的原因。 + 2146 | 你应该注意到了, 在 `combo-chart` 身上调用 `plot` 方法并没有产生所要求的结果。它只绘制了一个序列。 + 2147 | 发生了什么事? + 2148 | 2149 | `combo-chart` 继承自 `line-chart` 和 `bar-chart`, 它们都有一个叫做 `plot` 的方法。当我们在 `combo-chart` 身上调用那个方法时, Raku 内部会尝试通过调用其所继承的方法之一来解决冲突。 2150 | 2151 | .纠正 2152 | 2153 | 为了表现得正确, 我们应该在 `combo-chart` 中重写 `plot` 方法。 2154 | 2155 | [source,perl6] 2156 | ---- 2157 | class bar-chart { 2158 | has Int @.bar-values; 2159 | method plot { 2160 | say @.bar-values; 2161 | } 2162 | } 2163 | 2164 | class line-chart { 2165 | has Int @.line-values; 2166 | method plot { 2167 | say @.line-values; 2168 | } 2169 | } 2170 | 2171 | class combo-chart is bar-chart is line-chart { 2172 | method plot { 2173 | say @.bar-values; 2174 | say @.line-values; 2175 | } 2176 | } 2177 | 2178 | my $actual-sales = bar-chart.new(bar-values => [10,9,11,8,7,10]); 2179 | my $forecast-sales = line-chart.new(line-values => [9,8,10,7,6,9]); 2180 | 2181 | my $actual-vs-forecast = combo-chart.new(bar-values => [10,9,11,8,7,10], 2182 | line-values => [9,8,10,7,6,9]); 2183 | say "实际的销售: "; 2184 | $actual-sales.plot; 2185 | say "预测的销售: "; 2186 | $forecast-sales.plot; 2187 | say "实际 vs 预测:"; 2188 | $actual-vs-forecast.plot; 2189 | ---- 2190 | 2191 | .`输出`(译注,截至2019.4.30,这段代码在实现中的输出与预期不一致,参见 https://github.com/hankache/perl6intro/issues/192 ) 2192 | 2193 | ---- 2194 | 实际的销售: 2195 | [10 9 11 8 7 10] 2196 | 预测的销售: 2197 | [9 8 10 7 6 9] 2198 | 实际 vs 预测: 2199 | [10 9 11 8 7 10] 2200 | [9 8 10 7 6 9] 2201 | ---- 2202 | 2203 | === Role 2204 | 2205 | *role* 也是属性和方法的集合,在这个意义上它和类(class)有些相似。 2206 | role 使用关键字 `role` 声明, 而想实现该 role 的类可以使用 `does` 关键字。 2207 | 2208 | .使用 role 重写多重继承的例子 2209 | [source,perl6] 2210 | ---- 2211 | role bar-chart { 2212 | has Int @.bar-values; 2213 | method plot { 2214 | say @.bar-values; 2215 | } 2216 | } 2217 | 2218 | role line-chart { 2219 | has Int @.line-values; 2220 | method plot { 2221 | say @.line-values; 2222 | } 2223 | } 2224 | 2225 | class combo-chart does bar-chart does line-chart { 2226 | method plot { 2227 | say @.bar-values; 2228 | say @.line-values; 2229 | } 2230 | } 2231 | 2232 | my $actual-sales = bar-chart.new(bar-values => [10,9,11,8,7,10]); 2233 | my $forecast-sales = line-chart.new(line-values => [9,8,10,7,6,9]); 2234 | 2235 | my $actual-vs-forecast = combo-chart.new(bar-values => [10,9,11,8,7,10], 2236 | line-values => [9,8,10,7,6,9]); 2237 | say "实际的销售: "; 2238 | $actual-sales.plot; 2239 | say "预测的销售: "; 2240 | $forecast-sales.plot; 2241 | say "实际 vs 预测:"; 2242 | $actual-vs-forecast.plot; 2243 | ---- 2244 | 2245 | 运行上面的脚本你会看到结果是一样的。 2246 | 2247 | 现在你问问自己, 如果 role 表现得像类的话那么它们的用途是什么呢? + 2248 | 2249 | 要回答你的问题, 修改第一个用于展示多重继承的脚本, 这个脚本中我们 _忘记_ 重写 `plot` 方法了。 2250 | 2251 | [source,perl6] 2252 | ---- 2253 | role bar-chart { 2254 | has Int @.bar-values; 2255 | method plot { 2256 | say @.bar-values; 2257 | } 2258 | } 2259 | 2260 | role line-chart { 2261 | has Int @.line-values; 2262 | method plot { 2263 | say @.line-values; 2264 | } 2265 | } 2266 | 2267 | class combo-chart does bar-chart does line-chart { 2268 | } 2269 | 2270 | my $actual-sales = bar-chart.new(bar-values => [10,9,11,8,7,10]); 2271 | my $forecast-sales = line-chart.new(line-values => [9,8,10,7,6,9]); 2272 | 2273 | my $actual-vs-forecast = combo-chart.new(bar-values => [10,9,11,8,7,10], 2274 | line-values => [9,8,10,7,6,9]); 2275 | say "Actual sales:"; 2276 | $actual-sales.plot; 2277 | say "Forecast sales:"; 2278 | $forecast-sales.plot; 2279 | say "Actual vs Forecast:"; 2280 | $actual-vs-forecast.plot; 2281 | ---- 2282 | 2283 | .`输出` 2284 | 2285 | ---- 2286 | ===SORRY!=== 2287 | Method 'plot' must be resolved by class combo-chart because it exists in multiple roles (line-chart, bar-chart) 2288 | 类'combo-chart'的方法'plot'必须被决定,因为存在多个role(line-chart, bar-chart) 2289 | ---- 2290 | 2291 | .解释 2292 | 2293 | 如果多个 role 被应用到同一个类中, 会出现冲突并抛出一个编译时错误。 + 2294 | 这是比多重继承更安全的方法, 其中冲突不被认为是错误并且简单地在运行时解决。 2295 | 2296 | role 会提醒你有冲突。 2297 | 2298 | [source,perl6] 2299 | ---- 2300 | class Human { 2301 | has Str $.name; 2302 | has Int $.age; 2303 | method introduce-yourself { 2304 | say 'Hi I am a human being, my name is ' ~ self.name; 2305 | } 2306 | } 2307 | 2308 | class Employee is Human { 2309 | has Str $.company; 2310 | has Int $.salary; 2311 | method introduce-yourself { 2312 | say 'Hi I am a employee, my name is ' ~ self.name ~ ' and I work at: ' ~ self.company; 2313 | } 2314 | } 2315 | 2316 | my $john = Human.new(name =>'John',age => 23,); 2317 | my $jane = Employee.new(name =>'Jane',age => 25,company => 'Acme',salary => 4000); 2318 | 2319 | say $john.WHAT; 2320 | say $jane.WHAT; 2321 | say $john.^attributes; 2322 | say $jane.^attributes; 2323 | say $john.^methods; 2324 | say $jane.^methods; 2325 | say $jane.^parents; 2326 | if $jane ~~ Human {say 'Jane is a Human'}; 2327 | ---- 2328 | 2329 | === 内省 2330 | 2331 | *内省* 是获取诸如对象的类型、属性或方法等对象属性的信息的过程。 2332 | 2333 | [source,perl6] 2334 | ---- 2335 | class Human { 2336 | has Str $.name; 2337 | has Int $.age; 2338 | method introduce-yourself { 2339 | say 'Hi i am a human being, my name is ' ~ self.name; 2340 | } 2341 | } 2342 | 2343 | class Employee is Human { 2344 | has Str $.company; 2345 | has Int $.salary; 2346 | method introduce-yourself { 2347 | say 'Hi i am a employee, my name is ' ~ self.name ~ ' and I work at: ' ~ self.company; 2348 | } 2349 | } 2350 | 2351 | my $john = Human.new(name =>'John',age => 23,); 2352 | my $jane = Employee.new(name =>'Jane',age => 25,company => 'Acme',salary => 4000); 2353 | 2354 | say $john.WHAT; 2355 | say $jane.WHAT; 2356 | say $john.^attributes; 2357 | say $jane.^attributes; 2358 | say $john.^methods; 2359 | say $jane.^methods; 2360 | say $jane.^parents; 2361 | if $jane ~~ Human {say 'Jane is a Human'}; 2362 | ---- 2363 | 2364 | 内省使用了: 2365 | 2366 | * `.WHAT` 返回已经创建的对象所属的类。 2367 | 2368 | * `.^attributes` 返回一个包含该对象所有属性的列表。 2369 | 2370 | * `.^mtethods` 返回能在该对象身上调用的所有方法。 2371 | 2372 | * `.^parents` 返回该对象所属类的所有父类。 2373 | 2374 | * `~~` 叫做智能匹配操作符。 2375 | 如果对象是从它所进行比较的类或任何它继承的类创建的, 则计算为 True。 2376 | 2377 | [NOTE] 2378 | -- 2379 | 有关 Raku 中面向对象编程的更多信息,请参阅: 2380 | 2381 | * https://docs.raku.org/language/classtut 2382 | * https://docs.raku.org/language/objects 2383 | -- 2384 | 2385 | == 异常处理 2386 | 2387 | === 捕获异常 2388 | 2389 | *异常* 是当某些东西出错时发生在运行时的特殊行为。 + 2390 | 我们说异常被 _抛出_。 2391 | 2392 | 考虑下面这个运行正确的脚本: 2393 | 2394 | [source,perl6] 2395 | ---- 2396 | my Str $name; 2397 | $name = "Joanna"; 2398 | say "Hello " ~ $name; 2399 | say "How are you doing today?" 2400 | ---- 2401 | 2402 | .`输出` 2403 | 2404 | ---- 2405 | Hello Joanna 2406 | How are you doing today? 2407 | ---- 2408 | 2409 | 现在考虑这个抛出异常的脚本: 2410 | 2411 | [source,perl6] 2412 | ---- 2413 | my Str $name; 2414 | $name = 123; 2415 | say "Hello " ~ $name; 2416 | say "今天过得怎么样?" 2417 | ---- 2418 | 2419 | .`输出` 2420 | 2421 | ---- 2422 | Type check failed in assignment to $name; expected Str but got Int 赋值给$name时类型检查失败,预期Str但是得到Int 2423 | in block at exceptions.raku:2 2424 | ---- 2425 | 2426 | 你应该看到当错误出现时(在这个例子中把数组赋值给字符串变量)程序会停止并且其它行的代码不会被执行, 即使它们是正确的。 2427 | 2428 | *异常处理* 是捕获已经抛出的异常的过程以使脚本能继续工作。 2429 | 2430 | [source,perl6] 2431 | ---- 2432 | my Str $name; 2433 | try { 2434 | $name = 123; 2435 | say "Hello " ~ $name; 2436 | CATCH { 2437 | default { 2438 | say "请再说一次你的名字,我们在记录中找不到它。"; 2439 | } 2440 | } 2441 | } 2442 | say "今天过得怎么样?"; 2443 | ---- 2444 | 2445 | .`输出` 2446 | 2447 | ---- 2448 | 请再说一次你的名字,我们在记录中找不到它。 2449 | 今天过得怎么样? 2450 | ---- 2451 | 2452 | 异常处理是使用 `try-catch` 块完成的。 2453 | 2454 | [source,perl6] 2455 | ---- 2456 | try { 2457 | # 代码在这里运行 2458 | # 如果有东西出错, 脚本会进入到下面的 CATCH 块中 2459 | # 如果什么错误也没有, 那么 CATCH 块会被忽略 2460 | CATCH { 2461 | default { 2462 | # 只有抛出异常时, 这儿的代码才会被求值 2463 | } 2464 | } 2465 | } 2466 | ---- 2467 | 2468 | `CATCH` 块能像定义 `given` 块那样定义。 2469 | 这意味着我们能捕获并处理各种不同类型的异常。 2470 | 2471 | [source,perl6] 2472 | ---- 2473 | try { 2474 | # 代码在这里运行 2475 | # 如果有东西出错, 脚本会进入到下面的 CATCH 块中 2476 | # 如果什么错误也没有, 那么 CATCH 块会被忽略 2477 | CATCH { 2478 | when X::AdHoc { # 当异常 X::AdHoc 被抛出时要执行的操作 } 2479 | when X::IO { # 当异常 X::IO 被抛出时要执行的操作} 2480 | when X::OS { # 当异常 X::OS 被抛出时要执行的操作 } 2481 | default { # 当异常类型不属于上述任何一种时要执行的操作} 2482 | } 2483 | } 2484 | ---- 2485 | 2486 | === 抛出异常 2487 | 2488 | Perl 6 也允许你显式地抛出异常。 + 2489 | 有两种类型的异常可以抛出: 2490 | 2491 | * 特设异常(ad-hoc exceptions) 2492 | 2493 | * 类型化异常(typed exceptions) 2494 | 2495 | [source,perl6] 2496 | .特设 2497 | ---- 2498 | my Int $age = 21; 2499 | die "Error !"; 2500 | ---- 2501 | 2502 | [source,perl6] 2503 | .类型化 2504 | ---- 2505 | my Int $age = 21; 2506 | X::AdHoc.new(payload => 'Error !').throw; 2507 | ---- 2508 | 2509 | 使用 `die` 子例程后面跟着异常消息来抛出特设异常。 2510 | 2511 | 类型化异常是对象, 因此上面的例子中使用了 `.new()` 构造函数。 + 2512 | 所有类型化异常都是从类 `X` 开始, 下面是一些例子: + 2513 | `X::AdHoc` 是最简单的异常类型 + 2514 | `X::IO` 跟 IO 错误有关。 + 2515 | `X::OS` 跟 OS 错误有关。 + 2516 | `X::Str::Numeric` 跟把字符串强制转换为数字有关。 2517 | 2518 | NOTE: 查看异常类型和相关方法的完整列表请到 https://docs.raku.org/type-exceptions.html 2519 | 2520 | 2521 | 2522 | == 正则表达式 2523 | 2524 | 正则表达式(regular expression), 或 _regex_ 是一个用于模式匹配的字符序列。 + 2525 | (译注,regular expression和regex都是“正则表达式”,后者是前者的缩写。本文中的regex同时也是Perl6的类名,它包含“Perl6专属”的用法,与其他语言甚至Perl5中的正则语法不完全一致,故不翻译,以示区分。) 2526 | 2527 | 理解它最简单的一种方式是把它看作模式。 2528 | [source,perl6] 2529 | ---- 2530 | if 'enlightenment' ~~ m/ light / { 2531 | say "enlightenment 包含单词 light"; 2532 | } 2533 | ---- 2534 | 2535 | 在这个例子中, 智能匹配操作符 `~~` 用于检查一个字符串(enlightenment)是否包含一个单词(light)。 + 2536 | 2537 | "Enlightenment" 与regex `m/ light /` 匹配。 2538 | 2539 | === Regex 定义 2540 | 2541 | 正则表达式可以按如下方式定义: 2542 | 2543 | * /light/ 2544 | 2545 | * m/light/ 2546 | 2547 | * rx/light/ 2548 | 2549 | 除非显式地指定, 否则空白是无关紧要的, `m/light/` 和 `m/ light /` 是相同的。 2550 | 2551 | === 匹配字符 2552 | 字母数字字符和下划线 `_` 在表达式中是按原样写出的。 + 2553 | 所有其它字符必须使用反斜线或用引号围起来以转义。 2554 | 2555 | [source,perl6] 2556 | .反斜线 2557 | ---- 2558 | if 'Temperature: 13' ~~ m/ \: / { 2559 | say "提供的字符串包含冒号 :"; 2560 | } 2561 | ---- 2562 | 2563 | [source,perl6] 2564 | .单引号 2565 | ---- 2566 | if 'Age = 13' ~~ m/ '=' / { 2567 | say "提供的字符串包含等号 = "; 2568 | } 2569 | ---- 2570 | 2571 | [source,perl6] 2572 | .双引号 2573 | ---- 2574 | if 'name@company.com' ~~ m/ "@" / { 2575 | say "这是一个有效的电子邮件地址,因为它包含 @ 字符"; 2576 | } 2577 | ---- 2578 | 2579 | === 匹配字符类 2580 | 字符可以分类,我们可以用类别匹配字符。 + 2581 | 也可以匹配该类别的反面(除此之外的所有内容): 2582 | 2583 | |=== 2584 | 2585 | | *类别* | *Regex* | *反类别* | *Regex* 2586 | 2587 | | 单词字符 (字母数字下划线) | \w | 除了单词字符之外的任意字符 | \W 2588 | 2589 | | 数字 | \d | 除了数字之外的任意字符 | \D 2590 | 2591 | | 空白 | \s | 除了空白之外的任意字符 | \S 2592 | 2593 | | 水平空白 | \h | 除了水平空白之外的任意字符 | \H 2594 | 2595 | | 垂直空白 | \v | 除了垂直空白之外的任意字符 | \V 2596 | 2597 | | 制表符空白(Tab) | \t | 除了制表符空白之外的任意字符 | \T 2598 | 2599 | | 换行符 | \n | 除了换行符之外的任意字符 | \N 2600 | 2601 | |=== 2602 | 2603 | [source,perl6] 2604 | ---- 2605 | if "John123" ~~ / \d / { 2606 | say "这不是有效名称,不允许使用数字"; 2607 | } else { 2608 | say "这是个有效名称" 2609 | } 2610 | if "John-Doe" ~~ / \s / { 2611 | say "这个字符串包含空白"; 2612 | } else { 2613 | say "这个字符串不包含空白" 2614 | } 2615 | ---- 2616 | 2617 | === Unicode 属性 2618 | 就像之前章节看到的, 匹配字符类很方便。 + 2619 | 话虽这么说,更系统的方法是使用 Unicode 属性。 + 2620 | 这样就可以让你匹配在 ASCII 标准内和标准外的字符集。 2621 | Unicode 属性闭合在 `<: >` 中。 2622 | 2623 | [source,perl6] 2624 | ---- 2625 | if "Devanagari Numbers १२३" ~~ / <:N> / { 2626 | say "包含数字"; 2627 | } else { 2628 | say "不包含数字" 2629 | } 2630 | 2631 | if "Привет, Иван." ~~ / <:Lu> / { 2632 | say "包含大写字母"; 2633 | } else { 2634 | say "不包含大写字母" 2635 | } 2636 | 2637 | if "John-Doe" ~~ / <:Pd> / { 2638 | say "包含破折号"; 2639 | } else { 2640 | say "不包含破折号" 2641 | } 2642 | ---- 2643 | 2644 | === 通配符 2645 | 通配符也可以用在 regex 中。 2646 | 2647 | 点 `.` 意味着任何单个字符。 2648 | 2649 | [source,perl6] 2650 | ---- 2651 | if 'abc' ~~ m/ a.c / { 2652 | say "匹配"; 2653 | } 2654 | 2655 | if 'a2c' ~~ m/ a.c / { 2656 | say "匹配"; 2657 | } 2658 | 2659 | if 'ac' ~~ m/ a.c / { 2660 | say "匹配"; 2661 | } else { 2662 | say "不匹配"; 2663 | } 2664 | ---- 2665 | 2666 | === 量词 2667 | 2668 | 量词在字符后面用于指定我们期望匹配它前面的东西的次数。 2669 | 2670 | 问号 `?` 意思是 0 或 1 次。 2671 | 2672 | [source,perl6] 2673 | ---- 2674 | if 'ac' ~~ m/ a?c / { 2675 | say "匹配"; 2676 | } else { 2677 | say "不匹配"; 2678 | } 2679 | 2680 | if 'c' ~~ m/ a?c / { 2681 | say "匹配"; 2682 | } else { 2683 | say "不匹配"; 2684 | } 2685 | ---- 2686 | 2687 | 星号 `*` 意思是 0 或多次。 2688 | 2689 | [source,perl6] 2690 | ---- 2691 | if 'az' ~~ m/ a*z / { 2692 | say "匹配"; 2693 | } else { 2694 | say "不匹配"; 2695 | } 2696 | 2697 | if 'aaz' ~~ m/ a*z / { 2698 | say "匹配"; 2699 | } else { 2700 | say "不匹配"; 2701 | } 2702 | 2703 | if 'aaaaaaaaaaz' ~~ m/ a*z / { 2704 | say "匹配"; 2705 | } else { 2706 | say "不匹配"; 2707 | } 2708 | 2709 | if 'z' ~~ m/ a*z / { 2710 | say "匹配"; 2711 | } else { 2712 | say "不匹配"; 2713 | } 2714 | ---- 2715 | 2716 | `+` 意思是至少匹配 1 次。 2717 | 2718 | [source,perl6] 2719 | ---- 2720 | if 'az' ~~ m/ a+z / { 2721 | say "匹配"; 2722 | } else { 2723 | say "不匹配"; 2724 | } 2725 | 2726 | if 'aaz' ~~ m/ a+z / { 2727 | say "匹配"; 2728 | } else { 2729 | say "不匹配"; 2730 | } 2731 | 2732 | if 'aaaaaaaaaaz' ~~ m/ a+z / { 2733 | say "匹配"; 2734 | } else { 2735 | say "不匹配"; 2736 | } 2737 | 2738 | if 'z' ~~ m/ a+z / { 2739 | say "匹配"; 2740 | } else { 2741 | say "不匹配"; 2742 | } 2743 | ---- 2744 | 2745 | === 匹配结果 2746 | 2747 | 当匹配字符串的regex成功时, 2748 | 匹配结果被存储在一个特殊的变量 `$/` 中。 2749 | 2750 | [source,perl6] 2751 | .脚本 2752 | ---- 2753 | if 'Rakudo is a Raku compiler' ~~ m/:s Raku/ { 2754 | say "匹配的内容是:" ~ $/; 2755 | say "匹配之前的字符串:" ~ $/.prematch; 2756 | say "匹配之后的字符串:" ~ $/.postmatch; 2757 | say "匹配从字符串此处开始:" ~ $/.from; 2758 | say "匹配从字符串此处结束:" ~ $/.to; 2759 | } 2760 | ---- 2761 | 2762 | .输出 2763 | ---- 2764 | 匹配的内容是:Raku 2765 | 匹配之前的字符串:Rakudo is a 2766 | 匹配之后的字符串: compiler 2767 | 匹配从字符串此处开始:12 2768 | 匹配从字符串此处结束:18 2769 | ---- 2770 | 2771 | .解释 2772 | 2773 | `$/` 返回一个 _Match Object_ (匹配 regex 的字符串)。 + 2774 | 下面的方法可以在 _Match Object_ 身上调用: + 2775 | 2776 | `.prematch` 返回匹配前面的字符串 + 2777 | `.postmatch` 返回匹配后面的字符串 + 2778 | `.from` 返回匹配的开始位置 + 2779 | `.to` 返回匹配的结束位置 + 2780 | 2781 | TIP: 默认地,空白符在 regex 中会被忽略。 + 2782 | 如果想在 regex 中包含空白, 我们必须显式地这样做。 + 2783 | regex `m/:s Raku/` 中的 `:s` 用于强制匹配空白符。 2784 | 另外, 可以把 regex 写为 `m/Perl\s6/` ,使用 `\s` 代表空白符。 2785 | 如果 regex 中包含的空白不止一个, 使用 `:s` 比使用 `\s` 更高效。 2786 | 2787 | 2788 | 2789 | === 例子 2790 | 2791 | 让我们检查一个邮件是否合法。 + 2792 | 我们假设一个合法的电子邮件地址的形式如下: + 2793 | 名 [点] 姓 [at] 公司名 [点] (com/org/net) 2794 | 2795 | WARNING: 这个例子中用于电子邮件检测的 regex 不是很准确。 + 2796 | 它的核心意图是用来解释 Raku 中的 regex 的功能的。 + 2797 | 不要在生产中原样使用它。 2798 | 2799 | [source,perl6] 2800 | .脚本 2801 | ---- 2802 | my $email = 'john.doe@perl6.org'; 2803 | my $regex = / <:L>+\.<:L>+\@<:L+:N>+\.<:L>+ /; 2804 | 2805 | if $email ~~ $regex { 2806 | say $/ ~ " 是一个合法的Email地址"; 2807 | } else { 2808 | say "这不是合法的Email地址"; 2809 | } 2810 | ---- 2811 | 2812 | .输出 2813 | 2814 | `john.doe@perl6.org 是一个合法的Email地址` 2815 | 2816 | .解释 2817 | 2818 | `<:L>` 匹配一个字母 + 2819 | `<:L>+` 匹配至少一个字母 + 2820 | `\.` 匹配单个 . 符号 + 2821 | `\@` 匹配单个 @ 符号 + 2822 | `<:L+:N>` 匹配一个字母或数字 + 2823 | `<:L+:N>+` 匹配至少一个字母或数字 + 2824 | 2825 | 其中的 regex 可以分解成如下: 2826 | 2827 | * *名* `<:L>+` 2828 | 2829 | * *[点]* `\.` 2830 | 2831 | * *姓* `<:L>+` 2832 | 2833 | * *[at]* `\@` 2834 | 2835 | * *公司名* `<:L+:N>+` 2836 | 2837 | * *[点]* `\.` 2838 | 2839 | * *com/org/net* `<:L>+` 2840 | 2841 | [source,perl6] 2842 | .可选的, 一个 regex 可以被分解成多个具名 regex 。 2843 | ---- 2844 | my $email = 'john.doe@perl6.org'; 2845 | my regex 多个字母 { <:L>+ }; 2846 | my regex 点 { \. }; 2847 | my regex at { \@ }; 2848 | my regex 多个字母与数字 { <:L+:N>+ }; 2849 | 2850 | if $email ~~ / <多个字母> <点> <多个字母> <多个字母与数字> <点> <多个字母> / { 2851 | say $/ ~ " 是一个合法的Email地址"; 2852 | } else { 2853 | say "这不是合法的Email地址"; 2854 | } 2855 | ---- 2856 | 2857 | 具名 regex 是使用 `my regex 表达式名 { regex 定义 }` 定义的。 + 2858 | 具名 regex 可以使用 `<表达式名>` 来调用。 2859 | 2860 | NOTE: 更多关于 regex 的内容, 查看 https://docs.raku.org/language/regexes 2861 | 2862 | == Raku 模块 2863 | Raku是通用编程语言。 它可以用于处理众多任务,包括: 2864 | 文本处理,图形,网络,数据库,网络协议等。 2865 | 2866 | 可重用性是一个非常重要的概念,程序员不必在每次他们想要执行新任务时重新发明轮子。 2867 | 2868 | Raku 允许创建和重新分发 *modules*。 每个模块是一组封装的功能,可以在安装后重复使用。 2869 | 2870 | _Zef_ 是 Rakudo Star 中自带的模块管理工具。 2871 | 2872 | 要安装指定的模块, 在终端中键入如下命令: 2873 | 2874 | `zef install "module name"` 2875 | 2876 | NOTE: Raku 的模块目录可以在 https://modules.raku.org/ 中找到。 2877 | 2878 | === 使用模块 2879 | 2880 | MD5 是一个关于密码的散列函数,它产生一个128位的散列值。 + 2881 | MD5 有多种加密存储在数据库中的口令的应用程序。 2882 | 当新用户注册时,其证书并不存储为纯文本,而是 _哈希_。 2883 | 这样做的理由是,如果该数据库被破解,攻击者将不能够知道口令是什么。 2884 | 2885 | 比方说,你需要一个生成密码的MD5哈希以存储在数据库中备用的脚本。 2886 | 2887 | 幸运的是, Raku 已经有一个能实现 MD5 算法的模块。我们来安装它: + 2888 | `zef install Digest::MD5` 2889 | 2890 | 现在运行下面的脚本: 2891 | [source,perl6] 2892 | ---- 2893 | use Digest::MD5; 2894 | my $password = "password123"; 2895 | my $hashed-password = Digest::MD5.new.md5_hex($password); 2896 | 2897 | say $hashed-password; 2898 | ---- 2899 | 2900 | 为了运行创建哈希的 `md5_hex()` 函数, 我们需要加载需要的模块。 + 2901 | `use` 关键字用于在脚本中加载模块。 2902 | 2903 | WARNING: 实际上,MD5 哈希是不够的,因为它容易被字典攻击。 + 2904 | 它应该加盐。link:https://zh.wikipedia.org/wiki/%E7%9B%90_(%E5%AF%86%E7%A0%81%E5%AD%A6)[维基百科:盐 (密码学)] 2905 | 2906 | == Unicode 2907 | 2908 | Unicode 是编码并表现文本的标准, 它涵盖了世界上大多数书写系统。 + 2909 | UTF-8 是能够以Unicode编码所有可能的字符或代码点的字符编码。 2910 | 2911 | 字符的定义是通过: + 2912 | 2913 | *字素*: 视觉表示 + 2914 | *代码点*: 赋值给字符的数字 + 2915 | *代码点名称*: 字符的名称 2916 | 2917 | === 使用 Unicode 2918 | 2919 | .让我们看一下使用 Unicode 能输出什么 2920 | [source,perl6] 2921 | ---- 2922 | say "a"; 2923 | say "\x0061"; 2924 | say "\c[LATIN SMALL LETTER A]"; 2925 | ---- 2926 | 2927 | 上面 3 行展示了构建字符的不同方法: 2928 | 2929 | . 直接写出字符(字素) 2930 | 2931 | . 使用 `\x` 和代码点 2932 | 2933 | . 使用 `\c` 和代码点名字 2934 | 2935 | .现在我们来输出笑脸 2936 | [source,perl6] 2937 | ---- 2938 | say "☺"; 2939 | say "\x263a"; 2940 | say "\c[WHITE SMILING FACE]"; 2941 | ---- 2942 | 2943 | .组合两个代码点的另外一个例子 2944 | [source,perl6] 2945 | ---- 2946 | say "á"; 2947 | say "\x00e1"; 2948 | say "\x0061\x0301"; 2949 | say "\c[LATIN SMALL LETTER A WITH ACUTE]"; 2950 | ---- 2951 | 2952 | 字母 `á` 可以被写为: 2953 | 2954 | * 使用它的唯一代码点 `\x00e1` 2955 | 2956 | * 或作为 `a` 和 重音符号 `\x0061\x0301` 代码点的组合 2957 | 2958 | .有些方法可以使用 2959 | [source,perl6] 2960 | ---- 2961 | say "á".NFC; 2962 | say "á".NFD; 2963 | say "á".uniname; 2964 | ---- 2965 | 2966 | .`输出` 2967 | ---- 2968 | NFC:0x<00e1> 2969 | NFD:0x<0061 0301> 2970 | LATIN SMALL LETTER A WITH ACUTE 2971 | ---- 2972 | 2973 | `NFC` 返回唯一的代码点。 + 2974 | `NFD` 分解(decompose)那个字符并返回每部分的代码点。 + 2975 | `uniname` 返回代码点的名字。 2976 | 2977 | .Unicode 字符可以用作标识符: 2978 | [source,perl6] 2979 | ---- 2980 | my $Δ = 1; 2981 | $Δ++; 2982 | say $Δ; 2983 | ---- 2984 | 2985 | .Unicode 可以用作做算术: 2986 | [source,perl6] 2987 | ---- 2988 | my $var = 2 + ⅒; 2989 | say $var; 2990 | ---- 2991 | 2992 | === Unicode 相关的操作符 2993 | 2994 | ==== 数字 2995 | 2996 | 10 个阿拉伯数字 "0, 1, 2, 3, 4, 5, 6, 7, 8, 9" 是今天全世界使用最广泛的数字字符集。 2997 | 2998 | 不过在全世界各地其他数字字符集也少量地被使用。 2999 | 3000 | 在使用不同数字字符集的时候,不需要特别的留意。所有方法、操作符都会像在阿拉伯数字上那样工作。 3001 | 3002 | [source,perl6] 3003 | ---- 3004 | say (٤,٥,٦,1,2,3).sort; # (1 2 3 4 5 6) 3005 | say 1 + ٩; # 10 3006 | ---- 3007 | 3008 | ==== 字符串 3009 | 3010 | 我们在进行字符串操作的时候,结果可能不会像我们期待的那样,特别是在进行比较或排序的时候。 3011 | 3012 | ===== 比较 3013 | 3014 | [source,perl6] 3015 | ---- 3016 | say 'a' cmp 'B'; # More 3017 | ---- 3018 | 上面的例子显示 `a` 要比 `B` 更大。这是因为小写 `a` 的代码点比大写 `B` 的代码点大。 3019 | 3020 | 虽然这在技术上是对的,不过它可能不是我们所期待的。 3021 | 3022 | 幸运的是 Raku 拥有一套应用了 link:http://unicode.org/reports/tr10/[Unicode 排序算法]的操作符。 + 3023 | `unicmp` 是其中一个,它的功能和上面的 `cmp` 相似,不过是 unicode 相关的。 3024 | 3025 | [source,perl6] 3026 | ---- 3027 | say 'a' unicmp 'B'; # Less 3028 | ---- 3029 | 你看到了吗,使用 `unicmp` 操作符的时候得到 `a` 小于 `B` 的结果。 3030 | 3031 | ===== 排序 3032 | Raku 提供了 `collate` 方法来替代代码点排序方法 `sort` 应用于 unicode,`collate` 应用了 link:http://unicode.org/reports/tr10/[Unicode 排序算法]。 3033 | [source,perl6] 3034 | ---- 3035 | say ('a','b','c','D','E','F').sort; # (D E F a b c) 3036 | say ('a','b','c','D','E','F').collate; # (a b c D E F) 3037 | ---- 3038 | 3039 | 3040 | == 并行、并发和异步 3041 | 3042 | === 并行 3043 | 3044 | 在正常情况下, 程序中的所有任务都是相继地运行的。 + 3045 | 这可能不是个事儿除非你正尝试去做的东西需要耗费很多时间。 3046 | 3047 | 幸亏Raku 拥有能让你并行地运行的功能。 + 3048 | 此时, 需要注意到的是存在两类并行方式: 3049 | 3050 | * *任务并行化*: 两个(或更多)独立的表达式并行地运行。 3051 | 3052 | * *数据并行化*: 单个表达式并行地迭代列表中的元素。 3053 | 3054 | 让我们从后者开始。 3055 | 3056 | ==== 数据并行化 3057 | [source,perl6] 3058 | ---- 3059 | my @array = (0..50000); # 数组总体 3060 | my @result = @array.map({ is-prime $_ }); # 为每个数组元素调用 is-prime(判断是否为质数) 3061 | say now - INIT now; # 输出脚本完成花费的时间 3062 | ---- 3063 | 3064 | .考虑上面的例子: 3065 | 3066 | 我们只做一个操作 `@array.map({is-prime $_})`。 + 3067 | `is-prime` 子例程相继被每个数组元素所调用: + 3068 | `is-prime @array[0]` 然后是 `is-prime @array[1]` 然后是 `is-prime @array[2]` 等等。 3069 | 3070 | .幸运的是, 我们能同时在多个数组元素身上调用 `is-prime` 函数: 3071 | [source,perl6] 3072 | ---- 3073 | my @array = (0..50000); # 数组总体 3074 | my @result = @array.race.map({ is-prime $_ }); # 为每个数组元素调用 is-prime(判断是否为质数) 3075 | say now - INIT now; # 输出完成所花费的时间 3076 | ---- 3077 | 3078 | 注意表达式中使用的 `race`。这个方法会使数组元素能够并行地迭代。 3079 | 3080 | 运行两个例子(使用和不使用 `race`)运行之后, 比较两个脚本运行结束所花费的时间。 3081 | 3082 | [TIP] 3083 | ==== 3084 | `race` 不会保存元素的顺序。如果你想那样做, 使用 `hyper` 代替。 3085 | 3086 | [source,perl6] 3087 | .race 3088 | ---- 3089 | my @array = (1..1000); 3090 | my @result = @array.race.map( {$_ + 1} ); 3091 | @result».say; 3092 | ---- 3093 | 3094 | [source,perl6] 3095 | .hyper 3096 | ---- 3097 | my @array = (1..1000); 3098 | my @result = @array.hyper.map( {$_ + 1} ); 3099 | @result».say; 3100 | ---- 3101 | 3102 | 如果你俩个脚本都运行了, 你应该注意到一个排序了, 一个没有排序。 3103 | 3104 | ==== 3105 | 3106 | ==== 任务并行化 3107 | [source,perl6] 3108 | ---- 3109 | my @array1 = (0..49999); 3110 | my @array2 = (2..50001); 3111 | 3112 | my @result1 = @array1.map( {is-prime($_ + 1)} ); 3113 | my @result2 = @array2.map( {is-prime($_ - 1)} ); 3114 | 3115 | say @result1 == @result2; 3116 | 3117 | say now - INIT now; 3118 | ---- 3119 | 3120 | .考虑上面的例子: 3121 | 3122 | . 我们定义了 2 个数组 3123 | 3124 | . 对每个数组应用不同的操作并保存结果 3125 | 3126 | . 并检查两个结果是否相同 3127 | 3128 | 该脚本等到 `@array1.map( {is-prime($_ +1)} )` 完成 + 3129 | 然后计算 `@array1.map( {is-prime($_ +1)} )`。 3130 | 3131 | 应用到每个数组的俩个操作彼此间没有依赖。 3132 | 3133 | .为什么不并行地执行呢? 3134 | [source,perl6] 3135 | ---- 3136 | my @array1 = (0..49999); 3137 | my @array2 = (2..50001); 3138 | 3139 | my $promise1 = start @array1.map( {$_ + 1} ); 3140 | my $promise2 = start @array2.map( {$_ - 1} ); 3141 | 3142 | my @result1 = await $promise1; 3143 | my @result2 = await $promise2; 3144 | 3145 | say @result1 == @result2; 3146 | 3147 | say now - INIT now; 3148 | ---- 3149 | 3150 | .解释 3151 | 3152 | `start` 方法计算它后面的代码并返回 *promise 类型的对象* 或 *promise*。 + 3153 | 如果代码被正确地求值, 那么 _promise_ 会被 *保留*(kept)。 + 3154 | 如果代码抛出异常, 那么 _promise_ 会被 *破坏*(broken)。 3155 | 3156 | `await` 子例程等待一个 *promise*。 + 3157 | 如果那个 promise 是被 *保留* 的, await 会获取到返回值。 + 3158 | 如果那个 promise 是被 *破坏* 的, await 会获取到抛出异常。 3159 | 3160 | 检查每个脚本完成所花费的时间。 3161 | 3162 | WARNING: 并行总是添加线程开销。如果开销抵消不了运算速度的增长,那么该脚本会显得较慢。 + 3163 | 这就是为什么,在很简单的脚本中使用 `race`,`hyper`,`start` 和 `await` 实际上可以使它们慢下来。 3164 | 3165 | === 并发和异步 3166 | 3167 | NOTE: 关于并发和异步编程的更多信息, 请查看 https://docs.raku.org/language/concurrency 3168 | 3169 | == Native Calling 接口 3170 | Raku 可以让我们通过 Native Calling 接口来使用 C 库。 3171 | 3172 | `NativeCall` 是 Raku 自带的标准模块,它提供了一系列功能方便了 Raku 和 C 的接口。 3173 | 3174 | === 调用函数 3175 | 3176 | 下面的 C 代码定义了一个名为 `hellofromc` 的函数。 3177 | 这个函数的功能是在终端中打印 `Hello from C`。它不接收参数,也不返回值。 3178 | 3179 | [source,c] 3180 | .ncitest.c 3181 | ---- 3182 | #include 3183 | 3184 | void hellofromc () { 3185 | printf("Hello from C\n"); 3186 | } 3187 | ---- 3188 | 3189 | 根据你的操作系统将上面的 C 代码编译成库文件。 3190 | 3191 | .在 Linux 中: 3192 | ---- 3193 | gcc -c -fpic ncitest.c 3194 | gcc -shared -o libncitest.so ncitest.o 3195 | ---- 3196 | 3197 | .在 Windows 中: 3198 | ---- 3199 | gcc -c ncitest.c 3200 | gcc -shared -o ncitest.dll ncitest.o 3201 | ---- 3202 | 3203 | 在你编译 C 库的路径下新建一个包含下面代码的 Raku 文件,并运行它。 3204 | 3205 | [source,perl6] 3206 | .ncitest.raku 3207 | ---- 3208 | use NativeCall; 3209 | 3210 | constant LIBPATH = "$*CWD/ncitest"; 3211 | sub hellofromc() is native(LIBPATH) { * } 3212 | 3213 | hellofromc(); 3214 | ---- 3215 | 3216 | .解释 3217 | 首先,我们声明使用 `NativeCall` 模块。 + 3218 | 接着,我们定义了一个常量 `LIBPATH` 来存储 C 库的路径。 + 3219 | 其中 `$*CWD` 返回当前目录。 + 3220 | 然后,我们定义一个新的 Raku 子例程 `hellofromc` 作为 C 库中名称同为 `hellofromc` 的 C 函数之包装函数。这个 C 库就是 `LIBPATH` 所对应的。 + 3221 | 这些是通过使用 `is native` 特征实现的。 + 3222 | 最后,调用我们的 Perl6 子例程。 3223 | 3224 | 总而言之,整个过程即声明一个带有 `is native` 和 C 库名为特征的子程序。 3225 | 3226 | === 函数重命名 3227 | 3228 | 上面的例子中,我们看到了如何通过 `is native` 特征使用同名 Raku 子例程来包装 C 函数从而调用它。 3229 | 3230 | 有时我们希望更改 Raku 子例程的名称。 + 3231 | 为此,我们需要使用 `is symbol` 特征。 3232 | 3233 | 下面就来修改上面的 Raku 脚本,将 Raku 子例程 `hellofromc` 重命名为 `hello`。 3234 | 3235 | [source,perl6] 3236 | .ncitest.raku 3237 | ---- 3238 | use NativeCall; 3239 | 3240 | constant LIBPATH = "$*CWD/ncitest"; 3241 | sub hello() is native(LIBPATH) is symbol('hellofromc') { * } 3242 | 3243 | hello(); 3244 | ---- 3245 | 3246 | .解释 3247 | 由于 Raku 子例程与对应的 C 函数不同名,我们在这里需要使用 `is symbol` 来提供原始的 C 函数名。 3248 | 3249 | === 参数传递 3250 | 3251 | 编译下面更改过的的 C 库代码并运行 Raku 脚本。 + 3252 | 注意我们是如何修改 C 和 Raku 代码来接收一个字符串参数(在 C 中是 `chr*`,在 Raku 中是 `Str`) 3253 | 3254 | [source,c] 3255 | .ncitest.c 3256 | ---- 3257 | #include 3258 | 3259 | void hellofromc (char* name) { 3260 | printf("Hello, %s! This is C!\n", name); 3261 | } 3262 | ---- 3263 | 3264 | [source,perl6] 3265 | .ncitest.raku 3266 | ---- 3267 | use NativeCall; 3268 | 3269 | constant LIBPATH = "$*CWD/ncitest"; 3270 | sub hello(Str) is native(LIBPATH) is symbol('hellofromc') { * } 3271 | 3272 | hello('Jane'); 3273 | ---- 3274 | 3275 | === 返回值 3276 | 3277 | 让我们再来定义一个简单的计算器,实现接收两个整数输入并返回它们之和。 + 3278 | 编译下面的 C 库,并运行 Raku 脚本。 3279 | 3280 | [source,c] 3281 | .ncitest.c 3282 | ---- 3283 | int add (int a, int b) { 3284 | return (a + b); 3285 | } 3286 | ---- 3287 | 3288 | [source,perl6] 3289 | .ncitest.raku 3290 | ---- 3291 | use NativeCall; 3292 | 3293 | constant LIBPATH = "$*CWD/ncitest"; 3294 | sub add(int32,int32) returns int32 is native(LIBPATH) { * } 3295 | 3296 | say add(2,3); 3297 | ---- 3298 | 3299 | 注意其中 C 和 Raku 函数如何接收两个整数并返回一个整数。(C 中的 `int` 和 Raku 中的 `int32`) 3300 | 3301 | === 类型 3302 | 3303 | 你可能要问,为什么在最后的 Raku 脚本中我们要使用 `int32` 来代替 `Int` 。 + 3304 | 因为在 Raku 中像 `Int`,`Rat` 等类型不能用来传递与接收 C 函数中的值。 + 3305 | 所以必须在 Raku 中使用同 C 中类型相对应的类型。 3306 | 3307 | 幸运的是,Raku 提供了许多数据类型来对应 C 中的数据类型。 3308 | 3309 | [cols="^.^,^.^",options="header"] 3310 | |=== 3311 | 3312 | | C 类型| Raku 类型 3313 | 3314 | | `char` .2+| `int8` 3315 | 3316 | | `int8_t` 3317 | 3318 | | `short` .2+| `int16` 3319 | 3320 | | `int16_t` 3321 | 3322 | | `int` .2+| `int32` 3323 | 3324 | | `int32_t` 3325 | 3326 | | `int64_t` | `int64` 3327 | 3328 | | `unsigned char` .2+| `uint8` 3329 | 3330 | | `uint8_t` 3331 | 3332 | | `unsigned short` .2+| `uint16` 3333 | 3334 | | `uint16_t` 3335 | 3336 | | `unsigned int` .2+| `uint32` 3337 | 3338 | | `uint32_t` 3339 | 3340 | | `uint64_t` | `uint64` 3341 | 3342 | | `long` | `long` 3343 | 3344 | | `long long` | `longlong` 3345 | 3346 | | `float` | `num32` 3347 | 3348 | | `double` | `num64` 3349 | 3350 | | `size_t` | `size_t` 3351 | 3352 | | `bool` | `bool` 3353 | 3354 | | `char*` (String) | `Str` 3355 | 3356 | | Arrays: 比如 `int*` (Array of int) 和 `double*` (Array of double) | `CArray`: 比如 `CArray[int32]` 和 `CArray[num64]` 3357 | 3358 | |=== 3359 | 3360 | NOTE: 更多关于 Native Calling 接口, 详见 https://docs.raku.org/language/nativecall 3361 | 3362 | 3363 | == 社区 3364 | 3365 | * link:https://web.libera.chat/#raku[#raku] IRC 频道. 很多讨论发生在频道中。你可以到 https://raku.org/community/irc 进行任何询问。 3366 | 3367 | * link:https://rakudoweekly.blog[Rakudo Weekly] Raku 周边的变化和每周概述。 3368 | 3369 | * link:http://pl6anet.org[pl6anet] 博客聚合器. 敬请阅读专注于 Raku 的博客文章。 3370 | 3371 | * link:https://www.reddit.com/r/rakulang/[/r/rakulang] 订阅 Raku 子版本。 3372 | 3373 | * link:https://twitter.com/raku_news[@perl6org] Perl6 社区推特 3374 | 3375 | * link:https://alerts.perl6.org[P6lert] 核心开发者的提醒。及时了解重要的变动。 3376 | --------------------------------------------------------------------------------