tag:blogger.com,1999:blog-21799016119705932322024-03-12T19:56:39.524-04:00If you aren't HappyWhich Dwarf Are You?Tom Legradyhttp://www.blogger.com/profile/12348339971676149630noreply@blogger.comBlogger48125tag:blogger.com,1999:blog-2179901611970593232.post-10134505149868958802024-02-03T12:05:00.000-05:002024-02-03T12:06:35.435-05:00Book review: 390+ Python Interview Questions and Answers<span id="docs-internal-guid-65363746-7fff-4d7f-e0bc-f89bd1d8964c"><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: white; color: #484540; font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">I downloaded a preview portion of 390+ Python MCQs from Anazon, thinking reading through it would help me advance my Python skills beyond what I have learned from Harvard’s online CS50P (Python) course. I’m an experienced program looking to add a new skill to my repertoire, and while the course covered many significant aspects of Python programming, there are many other details to perfect, such as best practices, developing packages, and so on.</span></p><br /><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: white; color: #484540; font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">The book is written by Manish Dnyandeo Salunke, who claims 15 years experience in IT, but it is not clear who published it. It is obvious no one edited it, or verified the correctness of the questions, answers and explanations.</span></p><br /><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: white; color: #484540; font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">Amazon allowed me to download a sample of (I think) 57 questions. Roughly half of these were wrong, and some of the others struck me as irrelevant. The maximum allowed length for an identifier, apparently, is 79 characters. Anything over 20 characters should be considered unusual, so sufficient to say the limit is several times any useful size.</span></p><br /><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: white; color: #484540; font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">Here are my complaints … I wonder how many of the remaining 330+ questions are also wrong?</span></p><br /><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: white; color: #484540; font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">Question: What is the main difference between a .py file and a .pyc file?</span></p><ol style="margin-bottom: 0; margin-top: 0; padding-inline-start: 48px;"><li aria-level="1" dir="ltr" style="color: #484540; font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: decimal; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: white; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">.py files are source code files</span></p></li><li aria-level="1" dir="ltr" style="color: #484540; font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: decimal; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: white; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">.py files are bytecode files</span></p></li><li aria-level="1" dir="ltr" style="color: #484540; font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: decimal; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: white; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">.py files are compiled Python files</span></p></li><li aria-level="1" dir="ltr" style="color: #484540; font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: decimal; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: white; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">Py files are compressed archive files</span></p></li></ol><br /><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: white; color: #484540; font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">(in)Correct Response: 2</span><span style="background-color: white; color: #484540; font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"><span class="Apple-tab-span" style="text-wrap: nowrap;"> </span></span><span style="background-color: white; color: #484540; font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">SHOULD BE ‘1’</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: white; color: #484540; font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">Explanation: .py files contain Python source code, while .pyc files contain bytecode</span></p><br /><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: white; color: #484540; font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">Explanation is correct but response doesn’t match the options.</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 5pt; margin-top: 5pt;"></p><hr /><p></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: white; color: #484540; font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">Question: The process of converting source code into bytecode, which is then executed by the Python interpreter, is called ……</span></p><ol style="margin-bottom: 0; margin-top: 0; padding-inline-start: 48px;"><li aria-level="1" dir="ltr" style="color: #484540; font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: decimal; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: white; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">Compilation</span></p></li><li aria-level="1" dir="ltr" style="color: #484540; font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: decimal; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: white; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">Interpretation</span></p></li><li aria-level="1" dir="ltr" style="color: #484540; font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: decimal; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: white; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">Compilation</span></p></li><li aria-level="1" dir="ltr" style="color: #484540; font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: decimal; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: white; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">Execution</span></p></li></ol><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: white; color: #484540; font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">(in)Correct Response: 2</span><span style="background-color: white; color: #484540; font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"><span class="Apple-tab-span" style="text-wrap: nowrap;"> </span></span><span style="background-color: white; color: #484540; font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">SHOULD BE ‘1’ (or ‘3’, since the answer is shown twice)</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: white; color: #484540; font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">(their)Explanation: Python uses interpretation, not compilation, to execute code.</span></p><br /><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: white; color: #484540; font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">Actually, executing the code is achieved by interpreting the bytecode. The bytecode is obtained by compiling the source code. Ask google about Java bytecode, Python bytecode.</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 5pt; margin-top: 5pt;"></p><hr /><p></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: white; color: #484540; font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">Question: A colleague is trying to run a Python 2 script in a Python 3 environment, and the script fails due to a syntax error in the print statement. What would be the likely cause?</span></p><ol style="margin-bottom: 0; margin-top: 0; padding-inline-start: 48px;"><li aria-level="1" dir="ltr" style="color: #484540; font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: decimal; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: white; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">Python 2 uses print as a statement</span></p></li><li aria-level="1" dir="ltr" style="color: #484540; font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: decimal; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: white; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">Python 2 uses print as a function</span></p></li><li aria-level="1" dir="ltr" style="color: #484540; font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: decimal; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: white; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">Python 2 has a different file encoding</span></p></li><li aria-level="1" dir="ltr" style="color: #484540; font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: decimal; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: white; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">Python 2 requires a ‘__future__’ import</span></p></li></ol><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: white; color: #484540; font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">(in)Correct Response: 2</span><span style="background-color: white; color: #484540; font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"><span class="Apple-tab-span" style="text-wrap: nowrap;"> </span></span><span style="background-color: white; color: #484540; font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">SHOULD BE ‘1’</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: white; color: #484540; font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">(their)Explanation: …Python 2 uses print as a statement, while Python 3 uses it as a function …</span></p><br /><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: white; color: #484540; font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">The explanation is correct but the response doesn’t match the options.</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 5pt; margin-top: 5pt;"></p><hr /><p></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 5pt; margin-top: 5pt;"><span style="background-color: white; color: #484540; font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">Question: What will be the result of the comparison 5!=6 in Python?</span></p><ol style="margin-bottom: 0; margin-top: 0; padding-inline-start: 48px;"><li aria-level="1" dir="ltr" style="color: #484540; font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: decimal; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 5pt;"><span style="background-color: white; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">TRUE</span></p></li><li aria-level="1" dir="ltr" style="color: #484540; font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: decimal; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: white; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">FALSE</span></p></li><li aria-level="1" dir="ltr" style="color: #484540; font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: decimal; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: white; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">1</span></p></li><li aria-level="1" dir="ltr" style="color: #484540; font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: decimal; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.38; margin-bottom: 5pt; margin-top: 0pt;"><span style="background-color: white; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">Error</span></p></li></ol><p dir="ltr" style="line-height: 1.38; margin-bottom: 5pt; margin-top: 5pt;"><span style="background-color: white; color: #484540; font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">Correct Response: 1</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 5pt; margin-top: 5pt;"><span style="background-color: white; color: #484540; font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">Explanation: … the result is ‘True’</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 5pt; margin-top: 5pt;"><span style="background-color: white; color: #484540; font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">Complaint: Some languages use TRUE and FALSE - ALL CAPS - Python uses a capitalised first letter only. The right version is used in the explanation, but not shown in the options.</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 5pt; margin-top: 5pt;"></p><hr /><p></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 5pt; margin-top: 5pt;"><span style="background-color: white; color: #484540; font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">Silly questions about the logical operators to return True only if both values are True / if one value is True, and similar questions about comparison operators. Anyone who does not know such things needs to be looking for a book/course to train them in beginner Python, rather than in preparing them for interviews.</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 5pt; margin-top: 5pt;"></p><hr /><p></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 5pt; margin-top: 5pt;"><span style="background-color: white; color: #484540; font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">Question: Consider a scenario where you need to check if neither ‘a’ nor ‘b’ are true in Python. Which operator would you use?</span></p><ol style="margin-bottom: 0; margin-top: 0; padding-inline-start: 48px;"><li aria-level="1" dir="ltr" style="color: #484540; font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: decimal; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 5pt;"><span style="background-color: white; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">not a and not b</span></p></li><li aria-level="1" dir="ltr" style="color: #484540; font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: decimal; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: white; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">A and b</span></p></li><li aria-level="1" dir="ltr" style="color: #484540; font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: decimal; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: white; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">A or b</span></p></li><li aria-level="1" dir="ltr" style="color: #484540; font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: decimal; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.38; margin-bottom: 5pt; margin-top: 0pt;"><span style="background-color: white; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">not(a or b)</span></p></li></ol><p dir="ltr" style="line-height: 1.38; margin-bottom: 5pt; margin-top: 5pt;"><span style="background-color: white; color: #484540; font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">(in)Correct Response: 1</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 5pt; margin-top: 5pt;"><span style="background-color: white; color: #484540; font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">While 1 is a correct answer, so is 4, by deMorgan’s theorem. Having two correct answers is a minor error; listing only one of them as valid is a sin against the trusting consumer.</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 5pt; margin-top: 5pt;"></p><hr /><p></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 5pt; margin-top: 5pt;"><span style="background-color: white; color: #484540; font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">Question: What is the result of True or False and True?</span></p><ol style="margin-bottom: 0; margin-top: 0; padding-inline-start: 48px;"><li aria-level="1" dir="ltr" style="color: #484540; font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: decimal; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 5pt;"><span style="background-color: white; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">TRUE</span></p></li><li aria-level="1" dir="ltr" style="color: #484540; font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: decimal; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: white; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">FALSE</span></p></li><li aria-level="1" dir="ltr" style="color: #484540; font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: decimal; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: white; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">True and False is not allowed</span></p></li><li aria-level="1" dir="ltr" style="color: #484540; font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: decimal; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.38; margin-bottom: 5pt; margin-top: 0pt;"><span style="background-color: white; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">Error</span></p></li></ol><p dir="ltr" style="line-height: 1.38; margin-bottom: 5pt; margin-top: 5pt;"><span style="background-color: white; color: #484540; font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">(in)Correct Response: 2</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 5pt; margin-top: 5pt;"><span style="background-color: white; color: #484540; font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">(their nonsense) Explanation: The operation ‘True or False and True’ will result in an error because ‘and’ has a higher precedence than ‘or’, and ‘and’ is trying to combine a Boolean ‘False’ with a non-Boolean ‘True’</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 5pt; margin-top: 5pt;"><span style="background-color: white; color: #484540; font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">If their explanation were correct, the answer should be ‘4’. ‘2’ is also wrong. The correct answer is ‘1’. ‘And’ does have higher precedence, so False and True evaluate to False. Then True or False evaluate to True.</span><span style="background-color: white; color: #e2eeff; font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">The</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 5pt; margin-top: 5pt;"></p><hr /><p></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 5pt; margin-top: 5pt;"><span style="background-color: white; font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">Question: In the context of logical operators in Python, the not operator _______ the result.</span></p><ol style="margin-bottom: 0; margin-top: 0; padding-inline-start: 48px;"><li aria-level="1" dir="ltr" style="font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: decimal; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 5pt;"><span style="background-color: white; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">Inverts</span></p></li><li aria-level="1" dir="ltr" style="font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: decimal; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: white; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">Negates</span></p></li><li aria-level="1" dir="ltr" style="font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: decimal; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: white; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">Reverses</span></p></li><li aria-level="1" dir="ltr" style="font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: decimal; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.38; margin-bottom: 5pt; margin-top: 0pt;"><span style="background-color: white; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">Complements</span></p></li></ol><p dir="ltr" style="line-height: 1.38; margin-bottom: 5pt; margin-top: 5pt;"><span style="background-color: white; font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">(their) Correct Response: 1</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 5pt; margin-top: 5pt;"><span style="background-color: white; font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">But ‘negates’ and ‘complements’ are also correct answers.</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 5pt; margin-top: 5pt;"></p><hr /><p></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 5pt; margin-top: 5pt;"><span style="background-color: white; font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">Question: The ______ operator is used to determine if two values are not equal in Python.</span></p><ol style="margin-bottom: 0; margin-top: 0; padding-inline-start: 48px;"><li aria-level="1" dir="ltr" style="font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: decimal; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 5pt;"><span style="background-color: white; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">Unequal</span></p></li><li aria-level="1" dir="ltr" style="font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: decimal; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: white; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">Not equal</span></p></li><li aria-level="1" dir="ltr" style="font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: decimal; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: white; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">Inequality</span></p></li><li aria-level="1" dir="ltr" style="font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: decimal; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.38; margin-bottom: 5pt; margin-top: 0pt;"><span style="background-color: white; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">Difference</span></p></li></ol><p dir="ltr" style="line-height: 1.38; margin-bottom: 5pt; margin-top: 5pt;"><span style="background-color: white; font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">(Their) Correct response: 2</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 5pt; margin-top: 5pt;"><span style="background-color: white; font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">Answers ‘1’, ‘2’ and ‘3’ are essentially the same thing, but none of them is really the correct answer, which should be ‘!=’.</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 5pt; margin-top: 5pt;"></p><hr /><p></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 5pt; margin-top: 5pt;"><span style="font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">Question: A developer wants to compare two numbers and check if they are approximately equal ( with a small threshold…)</span></p><ol style="margin-bottom: 0; margin-top: 0; padding-inline-start: 48px;"><li aria-level="1" dir="ltr" style="font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: decimal; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 5pt;"><span style="font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">a ==b</span></p></li><li aria-level="1" dir="ltr" style="font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: decimal; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">abs(a-b) < epsilon</span></p></li><li aria-level="1" dir="ltr" style="font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: decimal; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">a is b</span></p></li><li aria-level="1" dir="ltr" style="font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: decimal; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.38; margin-bottom: 5pt; margin-top: 0pt;"><span style="font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">a !=b</span></p></li></ol><p dir="ltr" style="line-height: 1.38; margin-bottom: 5pt; margin-top: 5pt;"><span style="font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">(their) Response: 2</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 5pt; margin-top: 5pt;"><span style="font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">While correct, epsilon is unavailable without preparation. It requires importing the sys module.</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 5pt; margin-top: 5pt;"></p><hr /><p></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 5pt; margin-top: 5pt;"><span style="font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">Question: Given a scenario where a system should notify a user if the storage space goes below 5% or above 95% ….</span></p><ol style="margin-bottom: 0; margin-top: 0; padding-inline-start: 48px;"><li aria-level="1" dir="ltr" style="font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: decimal; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 5pt;"><span style="font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">If storage < 5 or storage > 95:</span></p></li><li aria-level="1" dir="ltr" style="font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: decimal; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">If storage <= 5 or storage >= 95:</span></p></li><li aria-level="1" dir="ltr" style="font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: decimal; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">If storage > 5 and storage < 95:</span></p></li><li aria-level="1" dir="ltr" style="font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: decimal; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.38; margin-bottom: 5pt; margin-top: 0pt;"><span style="font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">If 5 <= storage <= 95:</span></p></li></ol><p dir="ltr" style="line-height: 1.38; margin-bottom: 5pt; margin-top: 5pt;"><span style="font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">(their) Response: 2</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 5pt; margin-top: 5pt;"><span style="font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">The answer should be ‘1’. The requirements accept 5% and 95% as safe conditions, only outside those values is a problem.</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 5pt; margin-top: 5pt;"></p><hr /><p></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 5pt; margin-top: 5pt;"><span style="font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">Question: What keyword is used in Python to make a decision?</span></p><ol style="margin-bottom: 0; margin-top: 0; padding-inline-start: 48px;"><li aria-level="1" dir="ltr" style="font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: decimal; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 5pt;"><span style="font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">if</span></p></li><li aria-level="1" dir="ltr" style="font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: decimal; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">else</span></p></li><li aria-level="1" dir="ltr" style="font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: decimal; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">decide</span></p></li><li aria-level="1" dir="ltr" style="font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: decimal; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.38; margin-bottom: 5pt; margin-top: 0pt;"><span style="font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">choice</span></p></li></ol><p dir="ltr" style="line-height: 1.38; margin-bottom: 5pt; margin-top: 5pt;"><span style="font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">This question is apparently used to determine whether the applicant attended Python classes after the first day.</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 5pt; margin-top: 5pt;"></p><hr /><p></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 5pt; margin-top: 5pt;"><span style="font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">Question: For loops in Python use the _____ method internally to iterate over items.</span></p><ol style="margin-bottom: 0; margin-top: 0; padding-inline-start: 48px;"><li aria-level="1" dir="ltr" style="font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: decimal; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 5pt;"><span style="font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">Loop</span></p></li><li aria-level="1" dir="ltr" style="font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: decimal; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">Enumerate</span></p></li><li aria-level="1" dir="ltr" style="font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: decimal; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">Range</span></p></li><li aria-level="1" dir="ltr" style="font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: decimal; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.38; margin-bottom: 5pt; margin-top: 0pt;"><span style="font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">Iterate</span></p></li></ol><p dir="ltr" style="line-height: 1.38; margin-bottom: 5pt; margin-top: 5pt;"><span style="font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">(their) Response: 2</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 5pt; margin-top: 5pt;"><span style="font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">(their) Explanation: For loops in pYthon use the enumerate method internally to iterate over items. Enumerate returns both the index and the value of each item in the sequence, making it useful for tracking positions in the loop.</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 5pt; margin-top: 5pt;"><span style="font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">The answer should be ‘4’, or even better, ‘__iter__’. The question is about “internally”, while ‘enumerate’, while convenient, is used sometimes but not always at the user code level.</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 5pt; margin-top: 5pt;"></p><hr /><p></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 5pt; margin-top: 5pt;"><span style="font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">Question: You have a list of numbers and you want to compute the sum of all positive numbers only. Which control structure can be most beneficial to achieve this?</span></p><ol style="margin-bottom: 0; margin-top: 0; padding-inline-start: 48px;"><li aria-level="1" dir="ltr" style="font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: decimal; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 5pt;"><span style="font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">For loop</span></p></li><li aria-level="1" dir="ltr" style="font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: decimal; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">While loop</span></p></li><li aria-level="1" dir="ltr" style="font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: decimal; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">Conditional statement</span></p></li><li aria-level="1" dir="ltr" style="font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; list-style-type: decimal; vertical-align: baseline; white-space: pre;"><p dir="ltr" role="presentation" style="line-height: 1.38; margin-bottom: 5pt; margin-top: 0pt;"><span style="font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; text-wrap: wrap; vertical-align: baseline;">List comprehension</span></p></li></ol><p dir="ltr" style="line-height: 1.38; margin-bottom: 5pt; margin-top: 5pt;"><span style="font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">(their) Response: 3</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 5pt; margin-top: 5pt;"><span style="font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">(their) Explanation: …a conditional statement can be most beneficial. You can iterate through the list…</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 5pt; margin-top: 5pt;"><span style="font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;">You might as easily say that a loop ( for OR while ) is most beneficial. Within the loop check for positive numbers …. Actually, my choice would have been the list comprehension, since it combines everything into a compact assembly. Of cour there needs to be an ‘if’ clause … Essentially, the question is meaningless.</span></p><div><span style="font-family: Arial, sans-serif; font-size: 12pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"><br /></span></div></span>Tom Legradyhttp://www.blogger.com/profile/12348339971676149630noreply@blogger.com0tag:blogger.com,1999:blog-2179901611970593232.post-11712080595577528962021-02-10T19:45:00.003-05:002021-02-10T19:45:57.974-05:00Making a Re-Saw Saw<p>I made a framing saw from a kit at a Lee Valley workshop, and it was fabulous. It's great as a coping saw, cutting curves, but the thin blade makes it hard to cut a straight line.</p>
<div class="separator" style="clear: both;"><a href="https://1.bp.blogspot.com/-uGi1LiSYE3E/YCR8qistisI/AAAAAAAAsL4/Ue-dM0dSjkMezOSssJh1DxZwrgJEyXQuwCLcBGAsYHQ/s1615/kit_frame_saw.jpg" style="display: block; padding: 1em 0; text-align: center; "><img alt="" border="0" width="400" data-original-height="1211" data-original-width="1615" src="https://1.bp.blogspot.com/-uGi1LiSYE3E/YCR8qistisI/AAAAAAAAsL4/Ue-dM0dSjkMezOSssJh1DxZwrgJEyXQuwCLcBGAsYHQ/s400/kit_frame_saw.jpg"/></a></div>
<p>So I made another one on my own, using a chunk of bandsaw blade, and this was better for straight cuts. But trying to resaw a 1" thick boards into two 1/2" inch boards was impossible. The cut would wander all over the place. I would practically wind up with veneer.</p>
<div class="separator" style="clear: both;"><a href="https://1.bp.blogspot.com/-j3YGaG8RRtE/YCR8qgxZbNI/AAAAAAAAsL8/6ivS89dh45QPydqo-JQge1YAHEnXeIydQCLcBGAsYHQ/s1615/my_frame_saw.jpg" style="display: block; padding: 1em 0; text-align: center; "><img alt="" border="0" width="400" data-original-height="1211" data-original-width="1615" src="https://1.bp.blogspot.com/-j3YGaG8RRtE/YCR8qgxZbNI/AAAAAAAAsL8/6ivS89dh45QPydqo-JQge1YAHEnXeIydQCLcBGAsYHQ/s400/my_frame_saw.jpg"/></a></div>
<p>I saw a number of articles and YouTube videos about making a re-sawing saw, and I decided to make one. But I'm not keen on kits. For one thing I'm stingy, and don't like giving away the profit. Besides, it doesn't feel like I made it, if someone else provided the challenging parts. So I made my own.</p>
<div class="separator" style="clear: both;"><a href="https://1.bp.blogspot.com/-xMJSEO2_9jM/YCR8q5Om6dI/AAAAAAAAsMA/auz9n34-3Rcg_HOh9hhNGyKHcE4fUgK0QCLcBGAsYHQ/s295/resaw.jpg" style="display: block; padding: 1em 0; text-align: center; "><img alt="" border="0" width="400" data-original-height="221" data-original-width="295" src="https://1.bp.blogspot.com/-xMJSEO2_9jM/YCR8q5Om6dI/AAAAAAAAsMA/auz9n34-3Rcg_HOh9hhNGyKHcE4fUgK0QCLcBGAsYHQ/s400/resaw.jpg"/></a>
</div>
<p>The saw blade is cut from one of three rusty handsaw blades I got for $10 off Facebook Marketplace. I used an angle grinder to cut it out, a little over two inches wide, and to grind three-to-the-inch teeth.</p>
<p>The rectangular tubing I got for $20 from Metal Supermarket online - luckily they have a location nearby, so I picked it up at their location, no charge. I've always wanted a tap-and-die set, so I got one from the Marketplace to thread a hole for the tightening knob.</p>
<p>The long sides are poplar from the big box store, the ends and handles are from maple from a nearby hard-wood store.</p>
<p>The poplar sides are 11 1/2 inches outside to outside, so ten inches space in between. This is far too much, I'm never going to handle a board over five inches think. Five inches total space, forcing a maximum board thickness of 2 1/2 inches, should be plenty. Maybe even four inches and a fraction, allowing me to cut a four inch thick board into two two-inchers, might be sufficient.</p>
<div class="separator" style="clear: both;"><a href="https://1.bp.blogspot.com/-ad70Eqs4Z04/YCR8ql4RsGI/AAAAAAAAsL0/yQRHwJCVMJIfhU1TFy6v7YrPWdkbialTQCLcBGAsYHQ/s1211/handle.jpg" style="display: block; padding: 1em 0; text-align: center; "><img alt="" border="0" height="400" data-original-height="1211" data-original-width="908" src="https://1.bp.blogspot.com/-ad70Eqs4Z04/YCR8ql4RsGI/AAAAAAAAsL0/yQRHwJCVMJIfhU1TFy6v7YrPWdkbialTQCLcBGAsYHQ/s400/handle.jpg"/></a></div>
<p>I made the handles in imitation of James Wright, but it isn't necessary. With a five inch gap, and one-and-a-half inches for the two sides, the core width is 6 1/2 inches. Extend that by four or five inches on each side, and pare it down to roudinsh handles, should work fine, I think.</p>
Tom Legradyhttp://www.blogger.com/profile/12348339971676149630noreply@blogger.com0tag:blogger.com,1999:blog-2179901611970593232.post-18324801274730087132017-10-31T21:07:00.001-04:002017-10-31T21:09:58.169-04:00Implementing the Game with Perl & Moxie<style>
.example {
background-color:#ECFCFA;
line-height: 1.0;
border:1px solid gray;
font-family: courier new, courier, monospace;
}
p {line-height: 1.2;}
</style>
<p>
I've been creating classes relating to playing cards using the new Moxie module for the Perl programming language. The objective is to implement the card game <i>Go Fish!</i> as specified at <a href="http://rosettacode.org/wiki/Go_Fish">Rosetta Code</a>.</p>
<h2>The Outside-In View</h2>
<p>An actual program file should be simple; all the real code should be in testable modules. In this case, <code>play_go_fish.pl</code> takes this to an extreme.</p>
<pre><code><div class="example">#!/usr/bin/env perl
use warnings;
use strict;
use 5.026;
use lib '.';
use Game;
Game->new()->play();
</div></code></pre>
<p>As of Perl 5.26, the current directory is not automatically part of @INC, the search path for modules, so it is necessary to include it manually. That makes it possible to load the <code>Game</code> module, to instantiate an instance, and play a game.</p>
<pre><code><div class="example">package Game;
use Moxie;
use lib '.';
use Deck;
use Computer;
use Human;
use Const::Fast;
extends 'Moxie::Object';
const my @PLAYERS => qw( human computer );
const my $INITIAL_DEAL_COUNT => 9;
</div></code></pre>
<p>A <code>Game.pm</code> object begins like most other modules. We define a constant to specify the players; this will be useful in several places. The two players should take turns dealing, which affects who receives the first card, who gets the first chance to hunt for a card, but that's too much like work. I've settled for a fixed order.</p>
<pre><code><div class="example">has _deck => sub { Deck->new() };
sub deck : rw(_deck);
sub deal : handles(_deck->deal);
sub init : handles(_deck->init);
sub has_cards : handles(_deck->has_cards);
sub shuffle : handles(_deck->shuffle);
has _players => sub {
my $players = {
human => Human->new(),
computer => Computer->new() };
$players->{human}->name('human');
$players->{human}->opponent( $players->{'computer'} );
$players->{computer}->name('computer');
$players->{computer}->opponent( $players->{'human'} );
return $players;
};
sub players : ro(_players);
</div></code></pre>
<p>A game involves a deck of cards and two players, the human and the computer. Spoiler alert - the two players share much functionality, so they will be subclasses of a <code>Player.pm</code> base class.</p>
<p>The player objects are stored in a plain hash. Each player knows its name and has a reference to the opponent object. That makes it possible to ask the opponent for a rank of card, or to surrender cards if the opponent's guess matches cards in the player's hand.</p>
<pre><code><div class="example"># ......................................................................
#
sub deal_hands ($self) {
for my $Nth ( 1 .. $INITIAL_DEAL_COUNT ) {
for my $player (@PLAYERS) {
$self->players()->{$player}->add_card( $self->deal() );
}
}
$self->players()->{$_}->sort() for @PLAYERS;
return;
}
</div></code></pre>
<p>At the start of the game we deal out the initial hands, taking turns delivering a card to each player until they each have nine cards. The Hoyle web site disagrees with Rosetta Code, saying they should only get five cards, but it doesn't make much difference. When the cards are dealt, each hand is sorted by increasing rank.</p>
<pre><code><div class="example"># ......................................................................
# return TRUE-ish if game over, FALSE-ish if still playing.
#
sub find_winner ($self) {
# not over while either player has cards.
#
for my $player ( keys $self->players->%* ) {
return if $self->players->{$player}->has_cards;
}
my ( $hbooks, $cbooks ) =
( scalar $self->players()->{human}->books()->@*,
scalar $self->players()->{computer}->books()->@*,
);
return ( $hbooks > $cbooks ? 'human'
: $cbooks > scalar $hbooks ? 'computer'
: q{It's a tie!}
);
}
</div></code></pre>
<p>The game ends when the deck is empty and each player has run out of cards. So if either player has cards in their hand, we can return early. If they are both out of cards, then we compare the number of books - sets of all four cards of a rank - held by each player. The player with most books is the winner.</p>
<pre><code><div class="example"># ......................................................................
#
sub play ($self) {
$self->init;
$self->shuffle() unless $ENV{TESTING_ARTIFICIAL_GAME};
$self->deal_hands();
my $winner; # Can't win until deck used up
TURN:
while ( !$winner ) {
PLAY:
for my $player (@PLAYERS) {
if ( $ENV{TESTING_ARTIFICIAL_GAME} && $ENV{DEBUGGING} ) {
print "${player}'s hand: ";
$self->players->{$player}->print;
}
$self->players->{$player}->take_turn( $self->deck );
last PLAY unless $self->has_cards;
}
$winner = $self->find_winner();
}
for my $player (@PLAYERS) {
my $books = $self->players->{$player}->books;
say "$player has books: $books->@*.";
}
if ( -1 < index( $winner, 'tie' ) ) {
say "Game over! $winner"; # It's a tie!
}
else {
say "Game over! $winner wins!" # No tie, there's a winner.
}
} ## end sub play ($self)
1;
</div></code></pre>
<p>The first step is to install cards in the deck, and then to shuffle the cards. In the tests I re-define <code>init()</code> to install a predetermined set of cards, to verify the correct result is achieved. So in that case I don't want the deck shuffled; an environment variable handles that.
Then the hands can be dealt out.</p>
<p>Until someone wins, the players take turns. During debugging, I needed to monitor what cards were in each player's hand. Since it only takes effect when a couple of environment variables are defined, I let the code remain. The important part is that the player takes a turn, with control of the deck. Details of taking a turn are implemented in the <code>Player</code>, <code>Computer</code> & <code>Human</code> modules.</p>
<p>When a player runs out of cards after the deck is empty, the opponent must have just surrendered some cards, leading to an empty hand. After all, if a player had some cards but not a full book,
there is nowhere for the matching cards to be. The opponent and the deck are both empty.</p>
<p>At the end, the books held by each player are listed, and the winner - or a tie game - is declared.</p>
Tom Legradyhttp://www.blogger.com/profile/12348339971676149630noreply@blogger.com0tag:blogger.com,1999:blog-2179901611970593232.post-9217982960700443092017-10-22T12:33:00.000-04:002017-10-22T12:55:56.672-04:00Best of Seven Series<style>
.example {
background-color:#ECFCFA;
line-height: 1.0;
border:1px solid gray;
font-family: courier new, courier, monospace;
}
</style>
Mathematician Rob Bradley <a href="https://www.facebook.com/robertemmett/posts/10210364390133206?pnref=story">posted</a> that the <i>expected length</i> of a best-of-seven series should be about 5 3/4 games, on which basis he expected the New York Yankees to win. Or maybe living close to New York has warped his sense of reality.<br/>
<br/>
As a programmer, I felt a compulsion to verify his claim. I could figure out a formula, or list the possibilities (70 possible ways to win or lose 4 games out of 7), but I decided to write a simulation, using the Perl programming language. This explanation may be overly-detailed for programmers, but I want facebook friends with limited programming experience to understand.
<pre><code><div class="example">
sub count_games_in_series {
my ( $max ) = @_;
$max //= 7;
my ( $won, $lost) = (0, 0);
for my $games ( 1..7) {
if ( int rand 2 ) {
$won++;
}
else {
$lost++;
}
say "won $won; lost $lost; total $games" if $VERBOSE>1;
return $games if $won == 4 || $lost == 4;
}
return $max;
}
</div></code></pre>
In the <code>count_games_in_series</code> subroutine, I play a set of up to seven games, and return the number of games it took to have one or the other team win four. If the loo runs its full length, the return value is seven, but if the number of games won or the number of games lost reaches four before the full series, the routine returns early. There's a print statement that relies on a global variable that I used to debug and verify the code. The key bit of code is to generate a random number which is a fraction between zero and two, but not including two. When you discard the fractional part, you are left with an integer, either zero or one, which evaluate to false and true in the <i>if{}</i> test block.<br/>
<br/>
I might have made the condition simpler, but I wanted to be able to explore alternatives to 50-50 odds of winning.<br/.
<br/>
<pre><code><div class="example">
s ub main {
my $rounds = $ARGV[0] || 100;
$VERBOSE = $ARGV[1] || 0;
my ($games, %freq, $expected);
for my $round ( 1..$rounds ) {
my $g = count_games_in_series();
$freq{$g}++;
$games += $g;
print "$round\t$g\t$games\n" if $VERBOSE;
}
printf "After $rounds rounds, total %d games, average of %6.4f.\n",
$games, $games/$rounds;
print "games\tnumber\tpct\n";
for my $key ( sort keys %freq ) {
my $pct = $freq{$key}/$rounds;
$expected += $pct * $key;
print "$key, $freq{$key}, $pct\n";
}
my $int = int $expected;
my $fraction = $expected - int $int;
my $ratio = $fraction * 16;
say "expected value = $expected ... $int $ratio/16";
}
</div></code></pre>
In the main routine, I process the command line arguments, the first determining the how many time I run the <code>count_games_in_series</code> routine, the second to control whether debugging info is printed. Default behaviour is to run 100 loops, and not print anything.<br/>
<br/>
In the loop, I run the routine and each time update a variable to record the frequency of that number of games. Then I output the raw counts, and accumulated an expected value. Adding together the number of games multiplied by the percentage of times each count occurred results in the expected value. I format this is a integer and fraction over 16, since that's the way Rob had declared it.<br/>
<br/>
Running the simulation for 100,000,000 series generated the results:
<pre><code><div class="example">
$ perl best_of_seven.pl 100000000
After 100000000 rounds, total 581262599 games, average of 5.8126.
games number pct
4, 12494547, 0.12494547
5, 24997766, 0.24997766
6, 31258228, 0.31258228
7, 31249459, 0.31249459
expected value = 5.81262599 ... 5 13.00201584/16
</div></code></pre>
One time in eight, you would get a winner after four games. A quarter of the time, you have a winner after five games. But about one in three require six games, and about one in three require all seven. That's good for the fans and for the broadcasters and sports writers. They can agonize day after day about whether their favourite team will pull it off or not.<br/>
<br/>
But are the odds of winning any game really 50-50? Of course there are the relative qualities of the teams, and of the pitchers, but it's hard to model that in a 53 line program. But there is the concept of <i>home field advantage</i>. While it doesn't affect the physical aspects of the game, the psychological effects of being at home, of the cheering fans, and the detailed knowledge of the field's characteristics and oddities must have some effect.<br/>
<br/>
Of course home field advantage is a minor factor, but to test its effect I made it huge. The home team has a 90% chance of winning a game, the visiting team only 10%. In this case, you see a win after four games on 1.4% of the time, while 59% of series require the full seven games, for an average of 6.4 games. With a more moderate 75-25 breakdown, 7% of series are settled after four games while 38% go the full seven, for an expected length right on six games.<br/>
<br/>
So not surprisingly, a more realistic 55-45 home field advantage has little effect on the distribution, raising the expected value from 5.813 to 5.820
<br/><br/>
Tom Legradyhttp://www.blogger.com/profile/12348339971676149630noreply@blogger.com0tag:blogger.com,1999:blog-2179901611970593232.post-37117073405781268302017-10-13T14:23:00.002-04:002017-10-13T14:24:19.596-04:00Creating Perl5 Objects with Moxie<style>
.example {
background-color:#ECFCFA;
line-height: 1.0;
border:1px solid gray;
font-family: courier new, courier, monospace;
}
</style>
Having in the previous article prepared data types for car suits and card ranks, I can now combine them to provide a playing card class, using Stevan Little's Moxie module (version 0.04, so definitely early days.) The goal is to provide an object-oriented paradigm to the Perl 5 programming language which is more sophisticated, more powerful and less verbose than manually <var>bless()</var>-ing hashes. To achieve that goal it needs to be faster and light-weight compared to Moose. Currently, Moxie.pm and and MOP.pm are add-on modules, but eventually, when they are more complete, when the wrinkles have been ironed out, and when they have gained acceptance and a community of users, they might be merged into the Perl core.<br/>
<br/>
One significant feature of Moxie is that it reduces boilerplate code. You don't have to specify <code>warnigns</code> or <code>strict</code>. As well, the <code>features</code> or the perl you are using are enabled, among them <code>say</code>, <code>state</code>, <code>signatures</code>, and <code>post_deref</code>.
<br/>
<h2>A Simple Moxie Class</h2>
<pre><code><div class="example">
package Card {
use Moxie;
use Suits;
use Ranks;
extends 'Moxie::Object';
has _rank => sub { };
sub rank : ro(_rank);
has _suit => ( default => sub { });
sub suit : ro(_suit);
sub BUILDARGS : init_args( suit => _suit,
rank => _rank
);
# ...
}
</div></code></pre>
A package provides the namespace for a class. The crucial steps are to <var>use</var> the Moxie module; to declare we are a subclass of Moxie::Object, and potentially also of a some class we have defined; and to declare attributes and accessors. An attribute must specify a subroutine to provide a default value, in case it has not been initialized. In the example above I demonstrate both styles of doing this.
<br/>
<br/>
At the moment there is no mechanism to declare or to verify the type of an attribute, but I suspect that is inevitable at some point. It is not possible, at least currently, to have an attribute share a name with its <code>accessor</code>. The solution is to mark the attribute with a leading underscore, the conventional indicator for a private attribute. In fact the result of the code is a conventional blessed hash object, so it is possible to access the attribute directly: <code>$card->{_suit}</code>, but this is definitely not a <i>best practice</i>. If you're going to do that, why bother with Moxie in the first place?<br/>
<br/>
The <code>accessor</code> can be <code>read-only</code>, <code>write-only</code>, or <code>read-write</code>. The underlying hash element remains readable and writable. Besides the <code>accessor</code>, it is possible to define a <code>predicate</code>, to detect whether the attribute has been assigned or not, and a <code>clearer</code>, to reset an attribute to <code>undefined</code>. As well, a method can be marked to <code>delegate</code> its operation to the underlying attribute, invoking a method of the same name.<br/>
<br/>
The initalizer, <code>BUILDARGS</code> is shown with mandatory arguments. This is inevitable in this case since the attributes only have <code>read-only</code> accessors. An uninitialized object would remain so forever. But in cases where an attribute can be initialized in the constructor or can accept the default value, a '?' can be appended to the pair tag, to indicate the tag may or may not be present in the initializer list or pairs.<br/>
<h2>Completing the Class</h2>
<pre><code><div class="example">
my $FMT = "%2s%s";
sub format ( $self, $format = '' ) {
my $old_format = $FMT;
if ( $format eq ''
&& 0 == length ref $self )
{
$format = $self;
}
$FMT = $format
if $format;
return $old_format;
}
sub to_string ($self) {
return sprintf $FMT, $self->rank(), $self->suit();
}
sub print ($self) {
say $self->to_string();
}
sub compare_by_rank ( $self, $card ) {
return Ranks::compare( $self->rank(), $card->rank() ) # increasing rank
|| Suits::compare( $self->suit(), $card->suit() ) # increasing ruit
}
sub compare_by_suit ( $self, $card ) {
return Suits::compare( $self->suit(), $card->suit() ) # increasing ruit
|| Ranks::compare( $self->rank(), $card->rank() ) # increasing rank
}
sub gen_deck {
my @cards;
for my $suit ( Suits::suits() ) {
for my $rank ( Ranks::ranks() ) {
push @cards, Card->new( suit => $suit, rank => $rank );
}
}
return \@cards;
}
</div></code></pre>
Moxie can't do all the work, you have to write some code yourself. But once you no longer have to deal with repetitive implementation details, it becomes easier to focus on the important aspects of your class. And smaller code is easier to understand.<br>
<br>
In this case, I provide a method to express the card as a string, and a method to print that representation. The <code>to_string()</code> method will be important in other classes, as the basis for outputing a single card out of a set of cards. I'm using a class attribute <code>$FMT</code>, and I provide a setter/getter method, <code>format()</code>. This can be invoked as an object method, <code>$card->format()</code>, or as a class routine, <code>Card::format()</code>. If an argument is provided, that value is assigned to <code>$FMT</code>. In either case, the old value of the variable is returned to the user. While it might be possible to use a true attribute to specify the format for outputing a card, it would be irritating to have to asign each card of the deck with its format.<br/><br>
Two <code>compare</code> routines combine the routines provided in the Suits and Ranks modules. A hand should be displayed sorted. For games like Fish or Canasta which focus on collecting cards of a rank, you want cards sorted by rank and then by suit. Games like bridge sort cards by suit and then by rank. The actual selection of the appropriate compare routine will take in another class representing a collection of cards.<br/><br/>
Finally, a routine is provide to generate a deck of cards. It is convenient to provide it at this level, so that knowledge of and access to Suits and Ranks are limited to this one class.
<br/>Tom Legradyhttp://www.blogger.com/profile/12348339971676149630noreply@blogger.com0tag:blogger.com,1999:blog-2179901611970593232.post-47736938741842712122017-10-11T18:54:00.002-04:002017-10-13T14:24:19.604-04:00Perl5, Moxie and Enumurated Data Types<style>
.example {
background-color:#ECFCFA;
line-height: 1.0;
border:1px solid gray;
font-family: courier new, courier, monospace;
}
</style>
<h2>Moxie - a new object system for Perl5</h2>
Stevan Little created the Moose multiverse to upgrade the Perl 5 programming language's object-oriented system more in line with the wonderfull world of Perl 6. Unfortunately, it's grown into a bloated giant, which has inspired light-weight alternatives Moos, Moo, Mo, and others. Now he's trying to create a modern, efficient OO system that can become built into the language.<br />
<br />
I've seen a few of his presentations at YAPC (Yet Another Perl Conference, now known as TPC, The Perl Conference), among them <a href="https://www.youtube.com/watch?v=Srdwhdr5wPc">p5 mop final final v5 this is the last one i promise tar gz</a><. So I was delighted to recently see an announcement of the module Moxie, and decided to try implementing a card game.<br />
<br />
While the package provides some POD documentation about the main module, Moxie, it doesn't actually explain the <i>enum</i> package, Moxie::Enum. But delving into the tests directory reveals its secrets.<br />
<h2>Creating an Enum</h2>
<pre><code><div class="example">
package Ranks {
use Moxie::Enum;
enum by_ARRAY => qw( unused 2 3 4 5 6 7 8 9 10 J Q K A );
enum by_HASH => { 2 => 2, 3 => 3, 4 => 4, 5 => 5,
6 => 6, 7 => 7, 8 => 8, 9 => 9,
10 => 10, J => 11, Q => 12, K => 13,
A => 14 };
}
</div></code></pre>
In languages that provide enumerated types, the <i>enum</i> keyword precedes an <i>identifier</i> or <i>tag</i>, which is followed by an enumeration list. You can think of the enumeration list as being like a hash, each identifier maps to a value. If you declare the <i>enum</i> using a list such as <var>qw()</var>, the first element is assigned the value 1, with successive elements getting successive numbers. Note I wasted an <i>enum</i> so that the values would match up with the card values, at least until the face cards. If you want greater control over the values, you can declare the <i>enum</i> using a hash and explicitly specify the value for each tag. <br/><br/>
Once declared, you can access the keys and values of the <i>enum</i>, or the combination as hash, or you can check or access the value for a particular <i>enum</i> identifier.<br/>
<pre><code><div class="example">
# my %hash = Moxie::Enum::get_enum_for( PACKAGE => 'enum_TAG');
my %cards = Moxie::Enum::get_enum_for( Ranks => 'by_ARRAY' );
say "$_, $cards{$_}" for keys %cards
7, 7
unused, unused
6, 6
Q, Q
3, 3
2, 2
8, 8
5, 5
10, 10
J, J
4, 4
9, 9
K, K
A, A
say("$_, ", 0+$cards{$_}) for keys %cards
7, 7
unused, 1
6, 6
Q, 12
3, 3
2, 2
8, 8
5, 5
10, 10
J, 11
4, 4
9, 9
K, 13
A, 14
</div></code></pre>
It's interesting to note that the hash value is actually a dualvar; in a string context, it's the same as the key; in a numeric context, it's a number.<br/>
<pre><code><div class="example">
say join " - ", Moxie::Enum::get_keys_for( Ranks => RANKS )
4 - A - 9 - K - 6 - Q - 7 - unused - 8 - 10 - 5 - J - 3 - 2
say join " - ", Moxie::Enum::get_values_for( Ranks => RANKS )
6 - Q - 7 - unused - 8 - 5 - J - 10 - 2 - 3 - 4 - A - 9 - K
say join " - ", map {0+$_} Moxie::Enum::get_values_for( Ranks => RANKS )
4 - 14 - 9 - 13 - 6 - 12 - 7 - 1 - 8 - 11 - 5 - 10 - 2 - 3
say Moxie::Enum::has_value_for( Ranks => RANKS, 'K' )
1
say Moxie::Enum::get_value_for( Ranks => RANKS, 'K' )
K
say 0+ Moxie::Enum::get_value_for( Ranks => RANKS, 'K' )
13
</div></code></pre>
You can get the enum keys, i.e. tags, or the values, which you can force into a string or numeric context. You can check whether or not <var>Package => enum</var> contains a particular identifier, and what the associated value is. The only problem is the keys and values are returned in a non-deterministic order, while I would like cards to be sorted in some way until they are shuffled, and also to be able to compare and sort cards.<br/>
<br/>
To handle that, I add a routine to compare pairs of cards and another routine to return the list of keys sorted according to the values. Note that using the numeric comparison operator causes the enum values to be interpreted in a numeric context.<br/>
<pre><code><div class="example">
sub ranks {
return sort { compare( $a, $b ) }
grep { $_ ne 'unused' }
Moxie::Enum::get_keys_for( Ranks => 'RANKS');
}
sub compare {
return Moxie::Enum::get_value_for( Ranks => RANKS, $_[0])
<=> Moxie::Enum::get_value_for( Ranks => RANKS, $_[1]);
}
</div></code></pre>
<h2>Creating Another Enum</h2>
A deck of cards needs suits as well as ranks. And unicode makes it easy to use pretty pictures, though I haven't yet figures out how to make diamonds and hearts displayed in red, rather than black. Unfortunately, unicode provides the characters in a different order than the one I consider natural, but that can be dealt with.
<pre><code><div class="example">
# U+2660 ♠ Black Spade Suit
# U+2665 ♥ Black Heart Suit
# U+2666 ♦ Black Diamond Suit
# U+2663 ♣ Black Club Suit
package Suits {
use Moxie::Enum;
enum SUITS => qw( ♣ ♦ ♥ ♠ );
sub suits {
return sort { compare( $b, $a ) }
grep { $_ ne 'unused' }
Moxie::Enum::get_keys_for( Suits => 'SUITS');
}
sub compare {
return Moxie::Enum::get_value_for( Suits => 'SUITS', $_[0])
<=> Moxie::Enum::get_value_for( Suits => 'SUITS', $_[1]);
}
}
</div></code></pre>
The suits have to be declared in increasing order, clubs are least and spades are highest, so the numbers get assigned right. Then it's a simple matter to compare the values in <var>compare()</var>. Note the <var>suits()</var> routine uses <var>compare( $b, $a)</var> to get the cards with the highest ranked suit first.<br/>
<h2>Entering Unicode CHaracters</h2>
If you're wondering how to enter unicode characters, I used C-x 8 <enter> hex code <enter> in emacs. Actually, you can enter either a hex code or the unicode name. Of course, if you are going to use a character frequently, you would want to assign it tp a key cord ... emacs provides a surplus of those.
Tom Legradyhttp://www.blogger.com/profile/12348339971676149630noreply@blogger.com5tag:blogger.com,1999:blog-2179901611970593232.post-84586973255504327682017-09-28T15:16:00.000-04:002017-09-28T15:46:20.991-04:00Adventures in AutovivificationHaving recently started a new job, I was exposed to old code with multi-step tests against autovivification in multi-level hashes. You get used to the code you have seen, but in a new environment it‘s irritating and jarring.
<br />
Moose does not generally have the problem, first because class structure is pre-declared, because values are accessed using accessor functions rather than directly, and because responsibility is delegated down to attributes, avoiding long chains. On the other hand, Moose has it's own overhead, so hand-rolled objects, and bare structures still have their use.
<br />
If you don‘t protect against autovivification, then mis-spelling a key, or referencing keys which haven‘t been instantiated in this instance, causes those keys to instantly come into existence.
<br />
<div style="background-color:#ECFCFA; LINE-HEIGHT: 1.10; border:1px solid gray;">
<pre><code><span style="font-family: courier new, courier, monospace;"> #!/usr/bin/perl
use warnings;
use strict;
use Data::Dump 'dump';
use 5.024;
my $var = {key1 => {key2 => {key3 => 'a'}}};
say dump $var;
if ( $var->{key1}{key2}{key3b}[13]{foobarbaz} ) {
say 'Miracles DO happen!';
}
say dump $var;</span></code></pre></div>
<br />
This is ugly, and could potentially have more serious consequences.
<br />
<div style="background-color:#ECFCFA; LINE-HEIGHT: 1.10; border:1px solid gray;">
<pre><code><span style="font-family: courier new, courier, monospace;";> { key1 => { key2 => { key3 => "a" } } }
{
key1 => {
key2 => {
key3 => "a",
key3b => [
undef, undef, undef, undef,
undef, undef, undef, undef,
undef, undef, undef, undef,
undef, {},
],
},
},
}
</span></code></pre></div>
<br />
<h3>Manual Solutions</h3>
The manual solution is to assume the existence only of that portion which you can rely on, and then check stage by stage. I suppose in this case I should be verifying that <i>key3b</i> actually is an array, before determining the number of elements it contains, and whether element <i>13</i> stores a defined value. And perhaps the other elements should be tested for hash-y-ness. But generally you know what an element is, the question is whether it exists or not.
<br />
<div style="background-color:#ECFCFA; LINE-HEIGHT: 1.10; border:1px solid gray;">
<pre><code><span style="font-family: "courier new" , "courier" , monospace;"> my $var = {key1 => {key2=> {key3 => 'a'}}};
say dump $var;
if ( $var->{key1}{key2}{key3b}
&& 13 <= scalar @{ $var->{key1}{key2}{key3b} }
&& $var->{key1}{key2}{key3b}[13]
&& $var->{key1}{key2}{key3b}{foobarbaz} ) {
say 'Miracles DO happen!';
}
say dump $var;</span></code></pre></div>
<br />
It does lead to a clean data structure.<br />
<div style="background-color:#ECFCFA; LINE-HEIGHT: 1.10; border:1px solid gray;">
<pre><code><span style="font-family: "courier new" , "courier" , monospace;"> { key1 => { key2 => { key3 => "a" } } }
{ key1 => { key2 => { key3 => "a" } } }</span></code></pre></div>
<br />
But there‘s a two-fold price:<br />
<ul>
<li>many tests AND-ed together </li>
<li>many stages of element reference.</li>
</ul>
Each of these increases the possibility of an error, and complicates the task of reading and understanding the code.
<br />
One improvement is to create a brief alias to the portion of the structure you can rely on. Then the individual tests are shorter, clearer, and less likely to contain an error. And short tests make it easier to understand the set of tests.
<br />
<div style="background-color:#ECFCFA; LINE-HEIGHT: 1.10; border:1px solid gray;">
<pre><code><span style="font-family: "courier new" , "courier" , monospace;"> my $var = {key1 => {key2=>{key3 => 'a'}}};
say dump $var;
my $alias = $var->{key1}{key2};
if ( $alias->{key3b}
&& 13 < @{ $alias->{key3b} } && $alias->[13]
&& $alias->{key3b}{foobarbaz} ) {
say 'Miracles DO happen!';
}
say dump $var;</span></code></pre></div>
<br />
Creating a variable is wasteful and distracting. In the early 80s the programming language Turing had a keyword to achieve this, I believe it was <i>alias</i>. Perl already provides a very short alias that comes naturally to Perl programmers, $_.
<br />
You might use a <i>for</i> loop with a single element, to alias $_ to the reliable portion.
<br />
Or, although <i>smartmatch</i> itself can lead to problems, using <i>given</i> to assign $_ avoids the question of why there‘s a for loop, especially one with a single element. <i>given</i> is designed to assign $_; that‘s all it does. It is Perl's <i>alias</i>.
<br />
<div style="background-color:#ECFCFA; LINE-HEIGHT: 1.10; border:1px solid gray;">
<pre><code><span style="font-family: Courier New, Courier, monospace;"> no warnings 'experimental::smartmatch';
my $var = {key1 => {key2=>{key3 => 'a'}}};
say dump $var;
given ( $var->{key1}{key2} ) {
if ( $_->{key3b}
&& 13 < @{ $_->{key3b} } && $_->[13]
&& $_->{key3b}{foobarbaz} ) {
say 'Miracles DO happen!';
}
}
say dump $var;</span></code></pre></div>
<br />
<h3>Let's Use a Module</h3>
But none of these resolves the problem of the many tests.
<br />
The autovivification module comes to the rescue!
<br />
Faster than a speeding bullet! Able to leap tall buildings in a single bound! …
<br />
Oddly, the way you use the autovivification module is to say <code>no autovification</code>. After all, you want to prevent unintended autovivification, and how confusing would it be if you said <code>use autovivification</code> to disable autovivification.
<br />
<div style="background-color:#ECFCFA; LINE-HEIGHT: 1.10; border:1px solid gray;">
<pre><code><span style="font-family: "courier new" , "courier" , monospace;"> no autovivification;
my $var = {key1 => {key2=>{key3 => 'a'}}};
say dump $var;
if ( $var->{key1}{key2}{key3b}[13]{foobarbaz} ) {
say 'Miracles DO happen!';
}
say dump $var;</span></code></pre></div>
<br />
The result is what you want. The module does all the boring stuff for us, and prevents non-existent keys from suddenly being autovivified.
<br />
<div style="background-color:#ECFCFA; LINE-HEIGHT: 1.10; border:1px solid gray;">
<pre><code><span style="font-family: "courier new" , "courier" , monospace;"> { key1 => { key2 => { key3 => "a" } } }
{ key1 => { key2 => { key3 => "a" } } }</span></code></pre></div>
<br />
Besides protecting during a fetch, the default configuration also protects during an exists test.
<br />
<div style="background-color:#ECFCFA; LINE-HEIGHT: 1.10; border:1px solid gray; overflow-x: hidden">
<pre><code><span style="font-family: "courier new" , "courier" , monospace;"># old way
say "It's there"
if exists $var->{key1}{key2}{key3b}
&& 13 < @{ $var->{key1}{key2}{key3b}}
&& exists $var->{key1}{key2}{key3b}[13]
&& exists $var->{key1}{key2}{key3b}[13{foobarbaz};
# new way
say "It's there"
if exists $var->{key1}{key2}{key3b}[13]{foobarbaz};</span></code></pre></div>
<br />
... as well as during attempted deletes.
<br />
<div style="background-color:#ECFCFA; LINE-HEIGHT: 1.10; border:1px solid gray;">
<pre><code><span style="font-family: "courier new" , "courier" , monospace;"> # old way
delete $var->{key1}{key2}{key3b}[13]{foobarbaz}
if exists $var->{key1}{key2}{key3b}
&& 13 < @{ $var->{key1}{key2}{key3b}}
&& exists $var->{key1}{key2}{key3b}[13]
&& exists $var->{key1}{key2}{key3b}[13]{foobarbaz};
# new way
delete $var->{key1}{key2}{key3b}[13]{foobarbaz};</span></code></pre></div>
<br />
<code>autovivification.pm</code> can also prevent accidental assignment to unintended keys.
<br />
You can also specify in the <i>import</i> line... which in this case actually invokes the <i>unimport</i> routine...that it should protect against storing values into keys which do not already exist. If, for example, you are using hash-based data structures or traditional hash-based objects , you can create the struct or object with all the keys it will need. You can invoke <code>use autovivification qw<store></code> in the constructor, and protection will return when that goes out of scope.
<br />
<div style="background-color:#ECFCFA; LINE-HEIGHT: 1.10; border:1px solid gray;">
<pre><code><span style="font-family: "courier new" , "courier" , monospace;"> { key1 => { key2 => { key3 => "a" } } }
Can't vivify reference at 06_store.pl line 16.</span></code></pre></div>
<br />
Once the object exists, values of existing keys can be modified, but attempting to assign to a new key generates a fatal runtime error.
<br />
But, frankly, I don‘t mind taking responsibility for creating and modifying structures, so long as I‘m spared the task of checking component existence.
<br />
<h3>
Alternatives</h3>
<br />
The <code>sanity.pm</code> module, a competitor for <code>Modern::Perl</code>, <code>common::sense</code> and similar Boilerplate condensors, claims to provide autovivification protection.
<br />
<div style="background-color:#ECFCFA; LINE-HEIGHT: 1.10; border:1px solid gray;">
<pre><code>
<span style="font-family: "courier new" , "courier" , monospace;"> use sanity qw(Modern::Perl NO:autovivification);
use Data::Dump 'dump';
my $var = {key1 => {key2=>{key3 => 'a'}}};
say dump $var;
if ( $var->{key1}{key2}{key3b}[13]{foobarbaz} ) {
say 'Miracles DO happen!';
}
say dump $var;</span></code></pre></div>
<br />
Unfortunately, I couldn‘t get it to work properly. In any case, it uses the external autovivification module, so it only provides a benefit if you like its Boilerplate condensation. But it is nice to not have to type
<br />
<div style="background-color:#ECFCFA; LINE-HEIGHT: 1.10; border:1px solid gray;">
<pre> use warnings
use strict;
use 5.024;
# etc</pre>
</div>Tom Legradyhttp://www.blogger.com/profile/12348339971676149630noreply@blogger.com0tag:blogger.com,1999:blog-2179901611970593232.post-48734977352305888802015-06-24T20:02:00.002-04:002017-09-28T15:47:09.497-04:00Bash Inefficient Matrix MultiplicationBash can only handle integer arithmetic. To process floats, you need to shell out to 'bc' or some other external utility. The core multiplication changes to:<br />
<br />
<pre><code> result=`echo "$result + $m * $n" | bc`;
</code></pre>
<br />
Boy, does that slow things down! For each multiplication, you're launching a shell, invoking a program, returning results. The result is 800 multiplications a second. Note this is running on integer matrices, there remain unresolved problems processing floats.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://docs.google.com/spreadsheets/d/1ZXyR69JgQ7KuclntwxDUBmfQGrugdrz3wULLM9WoXcA/pubchart?oid=1424493994&format=image" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="197" src="https://docs.google.com/spreadsheets/d/1ZXyR69JgQ7KuclntwxDUBmfQGrugdrz3wULLM9WoXcA/pubchart?oid=1424493994&format=image" width="320" /></a></div>
<br />
One obvious problem is shelling out for every multiplication. More efficient is to accumulate the expression to calculate, and just invoke the external calculator once. Now we only shell out N^2 times, instead of N^3, so increasing matrix size leads to better performance, up to a limit. the 32x32 matrix gives 18x performance compared to the simpler version. This implies the 32x reduction in shelling out is offset by the cost of building the expression. By the time we reach 100x100, overhead costs are overwhelming gains: a 100x reduction in shelling out generates only a 16x improvement in performance.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://docs.google.com/spreadsheets/d/1ZXyR69JgQ7KuclntwxDUBmfQGrugdrz3wULLM9WoXcA/pubchart?oid=123465048&format=image" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="197" src="https://docs.google.com/spreadsheets/d/1ZXyR69JgQ7KuclntwxDUBmfQGrugdrz3wULLM9WoXcA/pubchart?oid=123465048&format=image" width="320" /></a></div>
<br />Tom Legradyhttp://www.blogger.com/profile/12348339971676149630noreply@blogger.com0tag:blogger.com,1999:blog-2179901611970593232.post-1467614235767896122015-06-23T17:02:00.001-04:002017-09-28T15:47:09.461-04:00BASH Matrix Multiplicationtl;dr Bash is not the language for math-intensive operations.<br />
<br />
<br />
<br />
<pre style="font-size: small;"><code>
REPS=$1;
FILE_1=$2;
FILE_2=$3
OUTFILENAME=$4;
readonly COLS=`head -1 $FILE_1 | wc -w`;
readonly ROWS=`cat $FILE_1 | wc -l`;
# echo "rows is $ROWS; cols is $COLS"
if [[ $ROWS != $COLS ]]; then
echo "Expecting square matrices, " \
</code> "but rows = $ROWS, cols = $COLS\n";</pre>
<pre style="font-size: small;"><code> exit 1;
fi
# --------------------------------------------------
# SUBROUTINES
#
function outputMatrix()
{
local matrixname=$1;
local matrix;
local elem;
echo "matrix is '$matrixname'.";
eval matrix=\( \${${matrixname}[@]} \);
local i=0;
for elem in "${matrix[@]}"; do
echo -n "$elem ";
if (( ++i == $COLS )); then
echo '';
i=0;
fi
done
}
function multiply()
{
declare -a product;
local M=$1 N=$2;
local i j k idx1 idx2 idx3;
for ((i=0; i < $ROWS; i++ )); do
for ((j=0; j<$COLS; j++)); do
local result=0;
for ((k=0; k<$ROWS; k++)); do
idx1=$((i*$ROWS+k));
idx2=$((k*$COLS+j));
result=$((result+${M}[idx1]*${N}[idx2]));
done
idx3=$((i*$COLS+j));
product[$idx3]=$result;
done
done
# outputMatrix product;
}
# --------------------------------------------------
#
matrix1=($(cat $FILE_1));
matrix2=($(cat $FILE_2));
start=`date +%s.%N`;
for ((repeat = 0; repeat < $REPS; repeat++)); do
multiply matrix1 matrix2
done
end=`date +%s.%N`;
echo "$end - $start" | bc
</code></pre>
<br />
My core multiplication routine is similar to the one at RosettaCode. Results are drastically inferior compared to Perl or JavaScript, a factor or 200x ~ 1000x as slow.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://docs.google.com/spreadsheets/d/1ZXyR69JgQ7KuclntwxDUBmfQGrugdrz3wULLM9WoXcA/pubchart?oid=2005858426&format=image" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="197" src="https://docs.google.com/spreadsheets/d/1ZXyR69JgQ7KuclntwxDUBmfQGrugdrz3wULLM9WoXcA/pubchart?oid=2005858426&format=image" width="320" /></a></div>
<br />
Notice that the 100x100 matrix has only a single data point, at 55,000 multiplications per second. That took 547 seconds; I wasn't going to wait around 2 hours for more repetitions.<br />
<br />
Part of the problem is from flattening a two-dimensional matrix into a single one-dimensional array. Converting indices into a single array index overwhelms the actual matrix multiplication, resulting in 3N^3 + N^2 multiplications. These all happen in "user space", while Perl & JS have the opportunity to carry out array indexing in "language space", potentially more efficient.<br />
<br />
Actually, simplifying the index calculations only improved runtime about 10%, not a whole lot. Possibly variable access overwhelms the difference between addition and multiplication.<br />
<br />
<pre style="font-size: small;"><code>
for ((i=0; i <; $ROWS; i++ )); do
for ((j=0; j < $COLS; j++)); do
local result=0;
local idx1=$((i*$ROWS));
local idx2=$j;
for ((k=0; k < $ROWS; k++)); do
result=$((result+${M}[idx1]*${N}[idx2]));
((idx1 += 1))
((idx2 += COLS))
done
product[$idx3]=$result;
((idx3 ++))
done
done
</code></pre>
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://docs.google.com/spreadsheets/d/1ZXyR69JgQ7KuclntwxDUBmfQGrugdrz3wULLM9WoXcA/pubchart?oid=2069419484&format=image" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="197" src="https://docs.google.com/spreadsheets/d/1ZXyR69JgQ7KuclntwxDUBmfQGrugdrz3wULLM9WoXcA/pubchart?oid=2069419484&format=image" width="320" /></a></div>
<br />
<br />Tom Legradyhttp://www.blogger.com/profile/12348339971676149630noreply@blogger.com0tag:blogger.com,1999:blog-2179901611970593232.post-33931269125100798532015-06-17T15:45:00.001-04:002017-09-28T15:47:09.482-04:00Better Performance Through TranspositionBoth Perl and Javascript implement only one-dimensional arrays. To represent a matrix you need to nest arrays within arrays, typically one array for each row, within the overall matrix array.<br />
<br />
Each element of the result matrix is the product of one row from the first matrix, and one column from the second. Going from one element to the next in the same row is much quicker than going to another row. The first matrix only changes rows once for each row, N changes, but the second matrix has N^3 changes of row, one for each multiplication.<br />
<br />
If we were to transpose the second array, turn the rows into columns and columns into rows, the multiplication could be carried out by by scanning along a row instead of going from row to row.That reduces the cost to N^2. How much will that save us?<br />
<br />
I transposed the second array while reading it in:<br />
<pre><code>
for my $size ( keys %vars ) {
my $filestub = q{M_} . $size . q{x} . $size . q{.};
my ( $f1, $f2 ) = ( $filestub . '1', $filestub . '2' );
$vars{$size}[0] = readMatrix( $f1 );
$vars{$size}[1] = transpose( readMatrix( $f2 ));
}</code></pre>
<pre><code>
</code></pre>
and use <span style="font-family: Courier New, Courier, monospace; font-size: x-small;">pairwise()</span> from <span style="font-family: Courier New, Courier, monospace; font-size: x-small;">List::MoreUtils</span> to multiply corresponding elements of the rows, and <span style="font-family: Courier New, Courier, monospace; font-size: x-small;">sum0()</span> from<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> List::Utils</span> to add up the products.<br />
<br />
<pre><code>
for my $i ( 0..$rows - 1 ) {
for my $j ( 0 .. $cols - 1 ) {
prod[$i][$j]
= sum0 pairwise { $a * $b } M->[$i], N->[$j];
}
}
</code></pre>
<br />
The results were much the same for both integers and floats.<br />
<pre><code>
<span style="font-size: x-small;">-➤ perl ./int_benchmark.pl
Processing for 5 seconds each size will take 25 seconds.
Wed Jun 17 15:25:14 2015
Rate M_100x100 M_32x32 M_10x10 M_5x5 M_2x2
M_100x100 5.54/s -- -97% -100% -100% -100%
M_32x32 174/s 3046% -- -97% -100% -100%
M_10x10 5114/s 92135% 2832% -- -85% -98%
M_5x5 34909/s 629461% 19913% 583% -- -87%
M_2x2 274124/s 4943582% 157052% 5260% 685% --
Wed Jun 17 15:25:46 2015
-➤ perl -E '
say "100 => ", 100**3 * 5.54 / 10**6;
say " 32 => ", 32**3 * 174 / 10**6;
say " 10 => ", 10**3 * 5114 / 10**6;
say " 5 => ", 5**3 * 34909 / 10**6;
say " 2 => ", 2**3 * 274124 / 10**6;
'
100 => 5.54
32 => 5.701632
10 => 5.114
5 => 4.363625
2 => 2.192992</span>
</code></pre>
<br />
Previous float results were 2.1, 4.1, 4.9, 5.2 and 5.4 MFLOPS for 2x2 ... 100x100 sized matrices. So 10,000 row changes or 1,000,000, it doesn't make much difference.<br />
<br />
I modified the Javascript program to operate on a transposed second matrix, but since Javascript doesn't provide a <span style="font-family: Courier New, Courier, monospace; font-size: x-small;">pairwise()</span> routine, I generated a <span style="font-family: Courier New, Courier, monospace; font-size: x-small;">dot-product()</span> routine which takes two rows. But both integer and float matrices produce much the same results as before ... +/- 10%. So transposing is irrelevant when there's so much other overhead in a scripting language.Tom Legradyhttp://www.blogger.com/profile/12348339971676149630noreply@blogger.com1tag:blogger.com,1999:blog-2179901611970593232.post-6716935553540207862015-06-15T23:07:00.000-04:002017-09-28T15:47:09.512-04:00Linpack FLOPS Benchmark<span style="font-family: inherit;">The numbers I've been reporting for Javascript and Perl performance need a context. Are they running on a Commodore 64, or a super-computer? Nope, it's my laptop, and here's what Linpack has to report about it: 4 core, 8 thread (Intel core i7) at 3.389 GHz. Peak performance is 46 GFLOPS. </span><br />
<div>
<span style="font-family: inherit;"><br /></span></div>
<span style="font-family: inherit;">Javascript and Perl are both single-threaded, so you only get 1/8 peak performance, 5 3/4 GFLOPS. That's still 1000 times the performance we actually achieve on more convenient languages. You have to sacrifice some things to gain benefits. On the optimistic side, a moderately fast modern computer sunning Perl or Javascript is almost as fast as Sparc-10 running LINPACK.</span><br />
<pre><code>
Intel(R) LINPACK data
Current date/time: Fri Jun 12 01:19:06 2015
CPU frequency: 3.389 GHz
Number of CPUs: 1
Number of cores: 4
Number of threads: 8
Parameters are set to:
Number of tests : 15
Number of equations to solve (problem size) : 1000 2000 5000 10000 15000 18000 20000 22000 25000 26000 27000 30000 35000 40000 45000
Leading dimension of array : 1000 2000 5008 10000 15000 18008 20016 22008 25000 26000 27000 30000 35000 40000 45000
Number of trials to run : 4 2 2 2 2 2 2 2 2 2 1 1 1 1 1
Data alignment value (in Kbytes) : 4 4 4 4 4 4 4 4 4 4 4 1 1 1 1
Maximum memory requested that can be used = 16200901024, at the size = 45000
============= Timing linear equation system solver =================
Size LDA Align. Time(s) GFlops Residual Residual(norm)
1000 1000 4 0.019 35.5628 1.029343e-12 3.510325e-02
1000 1000 4 0.018 36.2016 1.029343e-12 3.510325e-02
1000 1000 4 0.018 36.1857 1.029343e-12 3.510325e-02
1000 1000 4 0.018 36.2075 1.029343e-12 3.510325e-02
2000 2000 4 0.136 39.2932 4.298950e-12 3.739560e-02
2000 2000 4 0.135 39.4407 4.298950e-12 3.739560e-02
5000 5008 4 1.923 43.3503 2.581643e-11 3.599893e-02
5000 5008 4 1.925 43.3232 2.581643e-11 3.599893e-02
10000 10000 4 14.781 45.1176 9.603002e-11 3.386116e-02
10000 10000 4 14.825 44.9833 9.603002e-11 3.386116e-02
15000 15000 4 49.465 45.4959 2.042799e-10 3.217442e-02
15000 15000 4 49.541 45.4258 2.042799e-10 3.217442e-02
18000 18008 4 85.142 45.6727 2.894987e-10 3.170367e-02
18000 18008 4 85.978 45.2284 2.894987e-10 3.170367e-02
20000 20016 4 115.975 45.9938 4.097986e-10 3.627616e-02
20000 20016 4 116.138 45.9292 4.097986e-10 3.627616e-02
22000 22008 4 156.203 45.4512 4.548092e-10 3.331299e-02
22000 22008 4 156.091 45.4840 4.548092e-10 3.331299e-02
25000 25000 4 227.453 45.8025 6.089565e-10 3.462917e-02
25000 25000 4 227.769 45.7390 6.089565e-10 3.462917e-02
26000 26000 4 254.249 46.0913 6.669421e-10 3.506981e-02
26000 26000 4 254.840 45.9845 6.669421e-10 3.506981e-02
27000 27000 4 284.795 46.0804 6.672171e-10 3.253690e-02
30000 30000 1 414.983 43.3796 8.421348e-10 3.319704e-02
35000 35000 1 732.317 39.0347 1.085509e-09 3.151068e-02
40000 40000 1 1479.396 28.8428 1.466774e-09 3.262155e-02
45000 45000 1 2764.282 21.9782 1.711494e-09 3.011194e-02
Performance Summary (GFlops)
Size LDA Align. Average Maximal
1000 1000 4 36.0394 36.2075
2000 2000 4 39.3669 39.4407
5000 5008 4 43.3367 43.3503
10000 10000 4 45.0504 45.1176
15000 15000 4 45.4608 45.4959
18000 18008 4 45.4506 45.6727
20000 20016 4 45.9615 45.9938
22000 22008 4 45.4676 45.4840
25000 25000 4 45.7708 45.8025
26000 26000 4 46.0379 46.0913
27000 27000 4 46.0804 46.0804
30000 30000 1 43.3796 43.3796
35000 35000 1 39.0347 39.0347
40000 40000 1 28.8428 28.8428
45000 45000 1 21.9782 21.9782
End of tests
</code></pre>
<br />Tom Legradyhttp://www.blogger.com/profile/12348339971676149630noreply@blogger.com0tag:blogger.com,1999:blog-2179901611970593232.post-91611988916209365462015-06-15T17:27:00.002-04:002017-09-28T15:47:09.537-04:00Perl Floating Point-Multiplication BenchmarkI was worried whether I was making basic errors in testing the Perl version, so I decided to use the Benchmark module to get the numbers. I copied the matmult.pl file and added <span style="font-family: Courier New, Courier, monospace; font-size: x-small;">use Benchmark ':all'</span> to the header of the file. The <span style="font-family: Courier New, Courier, monospace; font-size: x-small;">main()</span> routine got changed to :<br />
<pre><code>
<span style="font-size: x-small;"> my $time = $ARGV[0] || 5;
my %vars = ( 2 => [],
5 => [],
10 => [],
32 => [],
100 => [],
);
for my $size ( keys %vars ) {
my $filestub = q{F_} . $size . q{x} . $size . q{.};
$vars{$size}[0] = readMatrix( $filestub . '1' );
$vars{$size}[1] = readMatrix( $filestub . '2' );
}
say "Processing for $time seconds each size ",
</span></code><span style="font-size: x-small;"> "will take @{[5 * $time]} seconds.";
</span><span style="font-size: x-small;"> say scalar localtime;
</span><span style="font-size: x-small;"> cmpthese( -$time, {
</span><span style="font-size: x-small;"> 'F_2x2' => sub { matmult( $vars{2}[0],
</span><span style="font-size: x-small;"> $vars{2}[1]); },
</span><span style="font-size: x-small;"> 'F_5x5' => sub { matmult( $vars{5}[0],
</span><span style="font-size: x-small;"> $vars{5}[1]); },
</span><span style="font-size: x-small;"> 'F_10x10' => sub { matmult( $vars{10}[0],
</span><span style="font-size: x-small;"> $vars{10}[1]); },
</span><span style="font-size: x-small;"> 'F_32x32' => sub { matmult( $vars{32}[0],
</span><span style="font-size: x-small;"> $vars{32}[1]); },
</span><span style="font-size: x-small;"> 'F_100x100' => sub { matmult( $vars{100}[0],
</span><span style="font-size: x-small;"> $vars{100}[1]); },
</span><span style="font-size: x-small;"> });</span></pre>
<pre><code><span style="font-size: x-small;"> say scalar localtime;
</span>
</code></pre>
<br />
The user can specify how long each variant should be run, with a default of 5 seconds for each size if no arg is provided.<br />
<pre><code>
<span style="font-size: x-small;">-➤ perl ./benchmark.pl 2
Processing for 2 seconds each size will take 10 seconds.
Mon Jun 15 17:11:08 2015
Rate F_100x100 F_32x32 F_10x10 F_5x5 F_2x2
F_100x100 5.39/s -- -97% -100% -100% -100%
F_32x32 163/s 2931% -- -97% -100% -100%
F_10x10 4909/s 90934% 2904% -- -85% -98%
F_5x5 32731/s 606904% 19929% 567% -- -88%
F_2x2 264660/s 4908131% 161856% 5292% 709% --
Mon Jun 15 17:11:22 2015
</span></code></pre>
<br />
Adjusting for the number of multiplications involved in each size of matrix --- 8 for the 2x2 up to 1,000,000 for the 100x100 --- produces results very similar to what we've already seen ( millions of multiplications per second):<br />
<br />
<pre><code>perl -E'say "100 => 5.36";
say "32 => ", 32000 * 154 / 10**6;
say "20 => ", 1000 * 4904 / 10**6;
say "5 => ", 125 * 32767 / 10**6;
say "2 => ", 8 * 263472 / 10**6;
'
100 => 5.39
32 => 5.216
20 => 4.909
5 => 4.091375
2 => 2.11728
</code></pre>
Rerunning the benchmarks with integer matrices and <span style="font-family: Courier New, Courier, monospace; font-size: x-small;">use integer</span> enabled generated insignificantly, fractionally better numbers; though I wonder if the pragma affected <span style="font-family: Courier New, Courier, monospace; font-size: x-small;">Benchmark's</span> calculations.Tom Legradyhttp://www.blogger.com/profile/12348339971676149630noreply@blogger.com2tag:blogger.com,1999:blog-2179901611970593232.post-29941395892854341602015-06-12T22:31:00.004-04:002017-09-28T15:47:09.475-04:00Perl Float Matrix MultiplicationFloating point performance compared to integer multiplication on Perl
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://docs.google.com/spreadsheets/d/1ZXyR69JgQ7KuclntwxDUBmfQGrugdrz3wULLM9WoXcA/pubchart?oid=586866058&format=image" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="246" src="https://docs.google.com/spreadsheets/d/1ZXyR69JgQ7KuclntwxDUBmfQGrugdrz3wULLM9WoXcA/pubchart?oid=586866058&format=image" width="400" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://docs.google.com/spreadsheets/d/1ZXyR69JgQ7KuclntwxDUBmfQGrugdrz3wULLM9WoXcA/pubchart?oid=1424053239&format=image" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="246" src="https://docs.google.com/spreadsheets/d/1ZXyR69JgQ7KuclntwxDUBmfQGrugdrz3wULLM9WoXcA/pubchart?oid=1424053239&format=image" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
This is much the same as Perl Integer performance, and disappointly inferior to Javascript. Not a whole lot worse, comparable, really, but <b>MY</b> language should be better than the <b>OTHER</b> language! :-)<br />
<div style="text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://docs.google.com/spreadsheets/d/1ZXyR69JgQ7KuclntwxDUBmfQGrugdrz3wULLM9WoXcA/pubchart?oid=645155653&format=image" imageanchor="1" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img border="0" height="246" src="https://docs.google.com/spreadsheets/d/1ZXyR69JgQ7KuclntwxDUBmfQGrugdrz3wULLM9WoXcA/pubchart?oid=645155653&format=image" width="400" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
Tom Legradyhttp://www.blogger.com/profile/12348339971676149630noreply@blogger.com1tag:blogger.com,1999:blog-2179901611970593232.post-14245912407592147072015-06-08T16:55:00.000-04:002017-09-28T15:47:09.455-04:00Perl Matrix MultiplicationHere's the Perl script for matrix multiplication. It's derived from the RosettaCode site with slight modifications ... I didn't like some things about their way of doing things, but it's much the same in the end.
<br />
<pre><code>
#! /usr/bin/perl5.20.2
use 5.020;
use warnings;
use strict;
use English;
use Time::HiRes qw(gettimeofday tv_interval);
use autodie;
use Data::Dumper;
# -------------------------------------------------------
# SUBROUTINES
#
# ⋯ ⋯ ⋯ ⋯ ⋯ ⋯ ⋯ ⋯ ⋯ ⋯ ⋯ ⋯ ⋯ ⋯ ⋯ ⋯ ⋯ ⋯ ⋯ ⋯ ⋯ ⋯ ⋯ ⋯ ⋯ ⋯ ⋯ ⋯
# matmult - multiple matrices. Copied from
# RosettaCode.orgwiki/Matrix_multiplication#Perl.
#
sub matmult {
my ( $M, $N ) = @_;
my ( $rows, $cols ) = (scalar @$M, scalar @{$N->[0]} );
my $prod = [];
my $k = @$N - 1;
for my $i ( 0..$rows - 1 ) {
for my $j ( 0 .. $cols - 1 ) {
$prod->[$i][$j] += $M->[$i][$_]
</code><code> * $N->[$_][$j]
for 0..$k;
}
}
return $prod;
}
# ⋯ ⋯ ⋯ ⋯ ⋯ ⋯ ⋯ ⋯ ⋯ ⋯ ⋯ ⋯ ⋯ ⋯ ⋯ ⋯ ⋯ ⋯ ⋯ ⋯ ⋯ ⋯ ⋯ ⋯ ⋯ ⋯ ⋯ ⋯
# readMatrix - read file data -> array of array of int.
#
sub readMatrix {
my ( $filename ) = @_;
open my $infile, '<', $filename;
local $/ = undef;
my ($matrix) = [map { [split / +/, $_] }
split / *\n/, <$infile>
];
return $matrix;
}
# ⋯ ⋯ ⋯ ⋯ ⋯ ⋯ ⋯ ⋯ ⋯ ⋯ ⋯ ⋯ ⋯ ⋯ ⋯ ⋯ ⋯ ⋯ ⋯ ⋯ ⋯ ⋯ ⋯ ⋯ ⋯ ⋯ ⋯ ⋯
# main line of processing
#
sub main {
die( "USAGE: $PROGRAM_NAME reps file1 file1" )
unless 3 == @ARGV
&& $ARGV[0] > 0
&& -e $ARGV[1] && -r _
&& -e $ARGV[2] && -r _;
my ($M, $N) = (readMatrix($ARGV[1]),
readMatrix($ARGV[2]));
# say Data::Dumper::Dumper [$M, $N];
my $t0 = [ gettimeofday ];
my $prod;
for my $reps ( 1 .. $ARGV[0] ) {
$prod = matmult( $M, $N );
}
my $delta = tv_interval($t0);
say "Multiplying matrices $ARGV[0] times takes ",</code></pre>
<pre><code> "$delta seconds.";
# say Data::Dumper::Dumper [$prod];
}
# -----------------------------------------------------
main();
</code></pre>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://docs.google.com/spreadsheets/d/1ZXyR69JgQ7KuclntwxDUBmfQGrugdrz3wULLM9WoXcA/pubchart?oid=933381131&format=image" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="246" src="https://docs.google.com/spreadsheets/d/1ZXyR69JgQ7KuclntwxDUBmfQGrugdrz3wULLM9WoXcA/pubchart?oid=933381131&format=image" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
Inverting the numbers to look at millions of multiplications per second, we see that overhead on small matrices degrades performance, but larger matrices enable 5.5 million multiplications per second.
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://docs.google.com/spreadsheets/d/1ZXyR69JgQ7KuclntwxDUBmfQGrugdrz3wULLM9WoXcA/pubchart?oid=645155653&format=image" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="246" src="https://docs.google.com/spreadsheets/d/1ZXyR69JgQ7KuclntwxDUBmfQGrugdrz3wULLM9WoXcA/pubchart?oid=645155653&format=image" width="400" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
Tom Legradyhttp://www.blogger.com/profile/12348339971676149630noreply@blogger.com0tag:blogger.com,1999:blog-2179901611970593232.post-45047891572504523962015-06-07T23:55:00.001-04:002017-09-28T15:47:09.490-04:00Node JS Multiplying Float MatricesI don't know why I wrote such stupid code for<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> readMatrix.js</span>. I guess when someone uses a new language their brain cells seize up, effective function becomes impossible.<br />
<br />
After tests I ran on Friday, I wanted to see how Javascript does with floating point numbers? JS only supports 64 bit floating-point numbers, but there must be special code in their to make integers look like integers ... unless their isn't.<br />
<br />
When I went to update my code to support numbers with fractions, my first thought was to flag whether a decimal point had been seen and then keep track of the value by which to divide, multiplying by ten for each fractional digit. I quickly realized I was sinking into the mire of coding stupidity; JS must have a function to convert strings into integers or floats, and indeed it has <span style="font-family: Courier New, Courier, monospace; font-size: x-small;">parseInt()</span> and<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"><b> </b>parseFloat()</span>. But in my research I discovered ( or re-discovered ) that there's no need for the conversion. Like Perl, JS allows arithmetic on the string representation of numbers:<br />
<br />
<pre> 1 + 2 ⇛ 3</pre>
<pre> "1" + "2"⇛ 3
</pre>
<br />
This made me think about how I would parse the file in Perl, using <span style="font-family: Courier New, Courier, monospace; font-size: x-small;">split()</span>. I knew JS had <span style="font-family: Courier New, Courier, monospace; font-size: x-small;">map()</span>, so quite likely it would have <span style="font-family: Courier New, Courier, monospace; font-size: x-small;">split()</span> as well. And it does! So all that manual processing went in the trash, and I simplified <span style="font-family: Courier New, Courier, monospace; font-size: x-small;">readMatrix.js</span> to<br />
<br />
<pre><code>
exports.readMatrix = function (err, data) {
if (err) {
throw err;
}
var rows = data.split(/ *\n/)
.filter(function(val) {
return val.length > 0;
});
var matrix = [], i, len;
for ( 1 = 0, len = rows.length; i<len; i++ ) {
matrix[i] = rows[i].split(/ +/);
}
return matrix;
};
</code></pre>
I think I had the two chunks merged, in an online TryIt editor I was experimenting with,but this is compact enough for the moment.<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://docs.google.com/spreadsheets/d/1ZXyR69JgQ7KuclntwxDUBmfQGrugdrz3wULLM9WoXcA/pubchart?oid=210636977&format=image" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="246" src="https://docs.google.com/spreadsheets/d/1ZXyR69JgQ7KuclntwxDUBmfQGrugdrz3wULLM9WoXcA/pubchart?oid=210636977&format=image" width="400" /></a></div>
<br />
{Correction - Although the original JS used only floats, including for integer values, Googles V* JS engine, the one used in node.js, has separate 32 bit integers.}<br />
<br />
<div style="text-align: left;">
<strike>Although integers are represented by 64-bit IEEE floating point numbers</strike>, integer multpiplication is still 3 times as fast as floating point multiplication. There are clearly optimizations that are being made use of to improve performance.</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Calculating average operations per second shows small matrixes have performance limited by other overhead, but larger matrices are pumping along at 7.8 million multiplications per second. </div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://docs.google.com/spreadsheets/d/1ZXyR69JgQ7KuclntwxDUBmfQGrugdrz3wULLM9WoXcA/pubchart?oid=807982706&format=image" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="246" src="https://docs.google.com/spreadsheets/d/1ZXyR69JgQ7KuclntwxDUBmfQGrugdrz3wULLM9WoXcA/pubchart?oid=807982706&format=image" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://docs.google.com/spreadsheets/d/1ZXyR69JgQ7KuclntwxDUBmfQGrugdrz3wULLM9WoXcA/pubchart?oid=809886566&format=image" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="246" src="https://docs.google.com/spreadsheets/d/1ZXyR69JgQ7KuclntwxDUBmfQGrugdrz3wULLM9WoXcA/pubchart?oid=809886566&format=image" width="400" /></a></div>
Tom Legradyhttp://www.blogger.com/profile/12348339971676149630noreply@blogger.com0tag:blogger.com,1999:blog-2179901611970593232.post-62909735897103554902015-06-05T02:04:00.000-04:002017-09-28T15:47:09.504-04:00Node JS As The Universal Shell Scripting SystemFor the past 20 years, I've focused on programming in <i>real</i> languages for <i>real</i> applications, as opposed to web stuff, :-). When I have dealt with the web, it's been using server-side scripts. Definitely not that Javascript crud. Mind you, back when I worked for a porn website, they also came up with a JS based website, Simple.com, long before other people were doing all-JS interfaces.<br />
<br />
But the time has come to re-evaluate ... well, everything. Javascript can be checked for validity and style using JSHint & JSLint; it can be debugged. There are frameworks and layers that make it possible to do useful things easily, treating old JS as a sort of widely-supported low-level language on which to implement more elegant systems.<br />
<br />
One of these new paradigms is the implementation of node.js to run JS programs on the server, rather than in a web page. Node uses the Google's V8 Javascript engine from the Chrome browser, which means a large corporation ensures high performance on Windows, Mac and Linux platforms powered by Intel 32 or 64 bit processors as well as ARM and MIPS processors. Node only has to handle the difference between a process and a web component: no <span style="font-family: Courier New, Courier, monospace;"><b>window</b></span> or <span style="font-family: Courier New, Courier, monospace;"><b>document</b></span> objects to access, but instead new <span style="font-family: Courier New, Courier, monospace;"><b>process</b></span> and <span style="font-family: Courier New, Courier, monospace;"><b>global</b></span> objects.<br />
<br />
And with the coming release of EcmaScript 6, the language itself will become far more modern and sensible.<br />
<br />
So if the language is tolerable to use, and available everywhere, perhaps it's time to start using it.BASH is great, but you need Cygwyn to use it on Windows. How does Node compare to Bash, or to Perl?<br />
<br />
I went to RosettaCode to look for implementations of problems in the required languages, and selected <a href="http://rosettacode.org/wiki/Matrix_multiplication" rel="nofollow" target="_blank">matrix multiplication</a> as the first challenge. I expect this to perform badly in a Unix shell script, but frankly, I don't generally use arrays in shell scripts, so we'll see how their solution works out.<br />
<br />
I used the <a href="http://rosettacode.org/wiki/Matrix_transpose#JavaScript" rel="nofollow" target="_blank">matrix transpose</a> and <a href="http://rosettacode.org/wiki/Matrix_multiplication#JavaScript" rel="nofollow" target="_blank">matrix multiplication</a> implementations to create a Matrix.js file. The one change is that I added:<br />
<br />
<span style="font-family: Courier New, Courier, monospace;">exports.Matrix = Matrix;</span><br />
<br />
to make available the constructor to the importing site; the other functions become available as methods on an object.<br />
<br />
I added a readMatrix() routine to import data from a file. If perhaps my code is inefficient, neither is it evaluated for performance. I only time the core multiplication. Here's my routine:<br />
<br />
<pre><code>
// Run the program as
// "DEBUGGING=1 node program args"
// to get debugging output
exports.readMatrix = function(err, data) {
if (err) {
throw err;
}
var row = 0,
col = 0,
i,len, num = 0;
matrix = [[]];
for ( i = 0, len = data.length; i < len; i++) {
switch ( data[i] ) {
case ' ' :
if ( num > 0 ) {
matrix[row][col] = num;
num = 0;
}
col++;
break;
case "\n" :
if ( num > 0 ) {
matrix[row][col] = num;
num = 0;
}
row++;
matrix[row] = [];
break;
case '1': case '2': case '3': case '4': case '5':
case '6': case '7': case '8': case '9': case '0':
num = num * 10 + (data[i] - '0');
break;
}
}
if ( num > 0 ) { // trailing value due to no final \n
matrix[row][col] = num;
}
else { // extra empty array due to final \n
if (matrix[row].length = == 0 ) {
matrix.splice(row, 1);
}
}
return matrix;
}
<code></code></code></pre>
This function will be used as the callback to a FileSystem readFile() function. It receives either an error message or the contents of the file. Errors are dealt with at the top, otherwise data is processed character by character. Numeric values are separated by spaces, so when a space is seen, the accumulated value, num, is stored in the current matrix cell. At the end of the line, a new empty row is begun, after storing the accumulator if necessary. For digits, the current contents of the accumulator are shifted over one decimal place, and the new digit added. Clearly, this only works with integers, and no error-proofing is provided, other than handling a trailing space at the end of a line. Superfluous blank lines would result in empty row arrays being added to the matrix, but we'll simply make sure that circumstance doesn't arise.
The top-level code is simple: Read in the above modules and the FileSystem module; read in the command line arguments, read in the actual matrices, and determine the time to perform the matrix multiplication <i>reps</i> times:
<br />
<pre><code>
var rm = require('./readMatrix.js');
var fs = require('fs');
var mat = require('./Matrix.js');
var reps = process.argv[2],
file1 = process.argv[3],
file2 = process.argv[4];
fs.readFile(file1, "utf-8", function readM(err, data) {
var M = new mat.Matrix(rm.readMatrix(err, data));
fs.readFile(file2, "utf-8", function readN(err, data) {
var N = new mat.Matrix(rm.readMatrix(err, data));
var t1 = process.hrtime();
var prod, i;
for ( i = 0; i < reps; i++ ) {
prod = M.mult(N);
}
var delta = process.hrtime(t1);
console.log( reps + " repetitions took " +
delta[0] +"."+delta[1]+" seconds");
});
});
</code></pre>
To begin, I multiplied two simple 2x2 matrices:
<br />
<pre> [ 1 2 ] [ 5 6 ] __\ [ 19 22 ]
[ 3 4 ] [ 7 8 ] / [ 32 50 ]
</pre>
The process took 0.11 seconds, but most of that is overhead. Doing it 10 times, or 100, or 1,000 or even 1,000,000 time took 0.11 ~ 0.47 seconds. Ten million iterations took 4.7 seconds, 100 million takes 49 seconds, one billion iterations takes 522 seconds. So that's pretty linear, and pretty fast. Of course the multiplication is insignificant, it's mostly overhead: function call, creating and discarding variables, garbage collection.
Since matrix multiplication is an N^3 operation, 2x2 matrices require 8 multiplication, while 5x5 matrices require 125. Increasing to a 5x5 matrix results in 1/8 the performance of a 2x2 matrix. 10x10 produces 1000 multiplications in 30 times as much time as 2x2. 32x32 matricies require 32768 multiplications to process, while 100x100 involve a million scalar multiplications.
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://docs.google.com/spreadsheets/d/1ZXyR69JgQ7KuclntwxDUBmfQGrugdrz3wULLM9WoXcA/pubchart?oid=555501770&format=image" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="246" src="https://docs.google.com/spreadsheets/d/1ZXyR69JgQ7KuclntwxDUBmfQGrugdrz3wULLM9WoXcA/pubchart?oid=555501770&format=image" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://docs.google.com/spreadsheets/d/1ZXyR69JgQ7KuclntwxDUBmfQGrugdrz3wULLM9WoXcA/pubchart?oid=809886566&format=image" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="246" src="https://docs.google.com/spreadsheets/d/1ZXyR69JgQ7KuclntwxDUBmfQGrugdrz3wULLM9WoXcA/pubchart?oid=809886566&format=image" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
My conclusion, even prior to having other language versions to compare with, is that Node JS provides excellent performance. You may want another language to implement MatLab or a nuclear power plant controller, but it is clear why it is being used for real applications, among them several IDE editors.
Tom Legradyhttp://www.blogger.com/profile/12348339971676149630noreply@blogger.com0tag:blogger.com,1999:blog-2179901611970593232.post-39409549795936648502014-03-18T22:49:00.003-04:002014-03-18T22:51:53.334-04:00Saint Patrick's Day plus one<div class="separator" style="clear: both; text-align: center;">
<a href="http://1.bp.blogspot.com/-Wq7Dm4KwF3o/UykE9ZD3Z5I/AAAAAAAAVno/CrzQwXRL6so/s1600/tl-StPatricksBalloons-3+sm.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://1.bp.blogspot.com/-Wq7Dm4KwF3o/UykE9ZD3Z5I/AAAAAAAAVno/CrzQwXRL6so/s1600/tl-StPatricksBalloons-3+sm.gif" height="320" width="320" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://1.bp.blogspot.com/-bXWGfBkU2MA/UykFJ7i2wZI/AAAAAAAAVn4/w2PWRJBCxXs/s1600/tl-StPatricksBalloons-1+sm.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://1.bp.blogspot.com/-bXWGfBkU2MA/UykFJ7i2wZI/AAAAAAAAVn4/w2PWRJBCxXs/s1600/tl-StPatricksBalloons-1+sm.gif" height="320" width="320" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://4.bp.blogspot.com/-CzrpHCmhWZw/UykFIj7rGRI/AAAAAAAAVnw/Ipzz-ZXBo6Q/s1600/tl-StPatricksBalloons-2+sm.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://4.bp.blogspot.com/-CzrpHCmhWZw/UykFIj7rGRI/AAAAAAAAVnw/Ipzz-ZXBo6Q/s1600/tl-StPatricksBalloons-2+sm.gif" height="320" width="320" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://3.bp.blogspot.com/-hIlezZDLsIM/UykFN7qnqEI/AAAAAAAAVoQ/yHIPJlrY-yA/s1600/tl-StPatricksBalloons-4+sm.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://3.bp.blogspot.com/-hIlezZDLsIM/UykFN7qnqEI/AAAAAAAAVoQ/yHIPJlrY-yA/s1600/tl-StPatricksBalloons-4+sm.gif" height="320" width="320" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://4.bp.blogspot.com/-lWJZ4dl6JPM/UykFM9esXiI/AAAAAAAAVoA/tAjIiyHGBMs/s1600/tl-StPatricksBalloons-5+sm.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://4.bp.blogspot.com/-lWJZ4dl6JPM/UykFM9esXiI/AAAAAAAAVoA/tAjIiyHGBMs/s1600/tl-StPatricksBalloons-5+sm.gif" height="320" width="320" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://4.bp.blogspot.com/-jitQvYnYv6E/UykFOA4tf2I/AAAAAAAAVoI/r3tZHlcyWV8/s1600/tl-StPatricksBalloons-6+sm.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://4.bp.blogspot.com/-jitQvYnYv6E/UykFOA4tf2I/AAAAAAAAVoI/r3tZHlcyWV8/s1600/tl-StPatricksBalloons-6+sm.gif" height="320" width="320" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://4.bp.blogspot.com/-zBj3b_YRWME/UykFP-B4odI/AAAAAAAAVoU/FbId6_4k2zA/s1600/tl-StPatricksBalloons-7+sm.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://4.bp.blogspot.com/-zBj3b_YRWME/UykFP-B4odI/AAAAAAAAVoU/FbId6_4k2zA/s1600/tl-StPatricksBalloons-7+sm.gif" height="320" width="320" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://2.bp.blogspot.com/-5tta-GvdqSs/UykFShflYbI/AAAAAAAAVog/H9gVA4zy6YE/s1600/tl-StPatricksBalloons-8+sm.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://2.bp.blogspot.com/-5tta-GvdqSs/UykFShflYbI/AAAAAAAAVog/H9gVA4zy6YE/s1600/tl-StPatricksBalloons-8+sm.gif" height="320" width="320" /></a></div>
<br />Tom Legradyhttp://www.blogger.com/profile/12348339971676149630noreply@blogger.com0tag:blogger.com,1999:blog-2179901611970593232.post-54252155527022836932013-10-26T16:21:00.001-04:002014-10-13T15:03:09.148-04:00Perl5i Presentation to Toronto Perl Mongers<p>I presented a workshop based on the blog articles I wrote in March & April about perl5i.</p>
<p>It went out on the Google Hangout, and the recording is available on <a href="http://www.youtube.com/watch?v=qSKbVm4SnUk">YouTube</a>, though unfortunately it stops short of the end. There may be a way that can be fixed, though i doubt it.</p>
<p>Slides are available at <a href="http://www.scribd.com/doc/179222736/Perl5i-pdf">Scribd</a>, as are the <a href="http://www.scribd.com/doc/179224145/Perl5i-notes-pdf">speaker notes</a>.</p>
<p>Thanks to Michael for the module, Ingy dot Net for his YAPC mention of my Rosetta Code efforts, and Stephan Little for p5mop, his attempts to bring OO into core Perl5.</p>Tom Legradyhttp://www.blogger.com/profile/12348339971676149630noreply@blogger.com0tag:blogger.com,1999:blog-2179901611970593232.post-4588691915126630702013-08-08T16:03:00.000-04:002013-08-08T16:03:08.831-04:00Dear John Nack #2I suppose I should name this <i>Dear Adobe</i>, since John isn't actually involved with Lightroom or Photoshop. And I will, next time.<br />
<br />
I <b><span style="background-color: yellow; color: blue; font-size: x-large;">LOVE</span></b> the <i>Adaptive Wide Angle</i> filter in Photoshop CC .... except when I don't.<br />
<br />
AWA is a filter like Lens Corrections, that places a preview of your image in window of its own, where you modify various controls and view the effects in more-or-less real time. When you click OK, the modifications are applied to the entire image. While there are a number of options, which vary the amount and type of controls available, I use it on panoramas. You draw a line on the image, by clicking the endpoints, and a guide line is presented, bisecting a circle. By clicking where the line and circle intersect, you can cause the line to rotate to a different angle. But unlike a simple image rotation, you can have many lines, rotated to various angles, manipulating a mesh superimposed on the image.<br />
<br />
I use it to straighten out panoramas, so my horizontals are horizontal and my verticals are all vertical. But you could be more creative with your selections, and generate something more cubist.<br />
<br />
I forgot to mention, since AWA is presumably intended for application to wide-angle images, the two points you select are connected by an arc, not a line, and the intensity of the arc depends on where you are in the image. That is, near the top the arc is a rainbow, near the bottom it is a cup, and in the middle it is a straight line.<br />
<h4>
Pre- & Post Arc Complexity</h4>
Sometimes the degree of curvature is an excellent match to the portion of the image I want to adjust, but sometimes the system wants to draw an arc, when I want a straight line, or at least a drastically different radius of curvature. I wish there were a way to control the curvature, maybe an adjustment in the sidebar. Actually, it's something you need to adjust in the middle of setting the end-points for a line. Perhaps a control-click could specify one or more intermediate points, similar to the way you draw bezier. In fact, why not allow bezier curves, not just arcs? Most people wouldn't use the fancier features, but a few <strike>perverts</strike> creative types would.<br />
<br />
As well, You don't always want a curve totally flattened out. Since wide-angle distortion has become a part of our visual vocabulary, there are times when you want to leave some distortion, or even to create some where there wasn't any! Wouldn't it be cool if you could adjust the post-flattening characteristics of the line, as well as the pre-flattening ones? Of course, you COULD achieve the same effect by using a "wrong" curve for the original line, then, when it was flattened, you would wind up with some curvature. In fact, all that's needed is to be able to re-adjust the points that define the curve, including the one or more intermediate points. Click the two endpoints of some feature to define a line, then drag the automatically provided mid-point away from the line, to introduce curvature where there was none.<br />
<h4>
Moving Reference Points away from Line Endpoints</h4>
A totally different problem I have is that sometimes the features I want to snap to, don't extend all the way across the region that should be modified. For example, if there's a wall with a couple of windows in it, the bottoms of the windows define a horizontal line ( usually ). But the line should extend right across the wall. AWA provides a zoom-in view of the endpoint, allowing you to precisely place the point where it belongs. But if you click on the windows, you aren't affecting the whole wall. If you go to the edge of the wall, there are no reference points to work by.<br />
<br />
My solution is to set an endpoint on one window, then drag across to the other end of the wall, setting the endpoint by guessing where the reference point is on the second window. Then I move the first endpoint to its end of the wall, again guessing where the reference point is.<br />
<br />
One simple solution would be to define the curve using the endpoints and intermediate point, and then extend the resulting line to the edges of the region. In effect, the end points are turned into intermediate points. I'm not sure how that would work with beziers.<br />
<br />
Another solution is to lock the preview window in one place (the window in the wall) while setting the endpoint at the end of the wall. The up-down location of the endpoint is critical, since it is defining a horizontal; the left-right position is less critical---it doesn't matter if it extends past the edge of the wall, or stops a fraction short. Perhaps you could have multiple preview windows, the way you have multiple reference points in the <i>Sharpen->Shake Reduction</i> filter.<br />
<h4>
Automating Line Angles</h4>
<div>
As I mentioned, the result of clicking twice on the image is a straight line which can be rotated to any angle you like. But it's irritating to rotate it some tiny amount. Long lines get better precision, while shorter lines are difficult to adjust accurately. Why can't you just specify that some line should become horizontal, or should become vertical, thought I? After imaging some overly-complicated ways that could be done, I thought the best solution would be to hold down the shift key, just like you do when drawing lines to restrict them to horizontal, vertical or diagonal. Well, that's a silly idea, but I gave it a try, anyway. What do you know, those Adobe engineers are almost as smart as I am! :-)</div>
<h4>
Endpoint Fine Adjustment</h4>
The preview gives you the ability to see close-in around the endpoint, but there is no fine-movement control, or, at least, I haven't found it. Once the proximity of the end-point is established, it would be good to set the exact location in a more precise manner, even pixel-by-pixel. I've tried chording the <u>shift</u>, <u>control</u>, <u>option</u> and <u>command</u> keys, but they don't seem to affect the behaviour.<br />
<h4>
Autoscale</h4>
When you go into AWA, the image occupies most of the frame. However, since it may not be rectangular, as a result of merging multiple images into a panorama, it may not fill it entirely, in both horizontal and vertical. On the contrary, while one dimension may not fill the frame, the other dimension frequently overflows it, like a camera viewfinder that shows you only 90% of the image.<br />
<br />
I prefer to work on the entire image, even if the edges may be not entirely useful, do to curvature and jaggedness. So I wish there were a button to set the scale so the entire image is visible.<br />
<br />
Once the work is done, the shape of the image may be altered somewhat, due to distorting the mesh. I want at least one dimension to totally fill the frame, during the processing from filter preview into actual image, since my superstitions tell me I get more effective pixels that way. I haven't tested it, so maybe I'm wrong. Still, it would be nice if there were <i>FIT</i> and <i>FILL</i> options, as there are in the Lightroom navigator, to set the scale to exactly the correct values. As it is, I type numbers into the scale adjustment, or scrub the slider, or use the arrows with or without the shift key. But it takes time, and sliding the image up or down, when it's something that computers should be doing for us. Of course, I may further optimize the scale, prior to applying the changes, if I'm intending to crop the image further.<br />
<br />
<h4>
Summary</h4>
<br />
So there's my list. One of my requests is already implemented. Are any others already there, just waiting for me to find the right commands? Will Adobe think I'm out-to-lunch, or will I be receiving royalty checks ($0.003 a year) for inventing brilliant ideas?Tom Legradyhttp://www.blogger.com/profile/12348339971676149630noreply@blogger.com0tag:blogger.com,1999:blog-2179901611970593232.post-42998698007051062522013-07-15T15:44:00.001-04:002013-07-16T16:44:25.646-04:00Dear John Nack #1Dear John.<br />
<br />
Please arrange for Adobe to implement the following feature for me.<br />
<br />
I love the "Visualize Spots" checkbox in the LightRoom / Camera Raw "Healing Brush" as a way to locate dust spots that need to be cleaned away.<br />
<br />
The problem is, you need to be zoomed in at 100% to effectively deal with sensor dust, and sometimes it's not clear whether a particular mark on the screen is a fault or a feature. Then you need to click to normal view, and back to the edge-detection view. It's a problem having to move the cursor down to the checkbox, back to the spot to clean away, and back to the checkbox. A key-chord combination to enable and disable the edge-detected view would be a great benefit ... and there are so many to choose from! It could be Ctrl-H, since it's a Hide operation; or like overflow/underflow indication, it could simply use the control key. Only while healing, you understand.<br />
<br />
That's all right, no royalties are necessary.<br />
<br />
Tom<br />
<br />
16 July .... Brilliant! John says to click the 'A' key when using the healing brush, to switch back and forth between the normal view and the edge-detection view. 'A' is for 'Admirable'!Tom Legradyhttp://www.blogger.com/profile/12348339971676149630noreply@blogger.com0tag:blogger.com,1999:blog-2179901611970593232.post-52406487578523969192013-05-12T22:22:00.000-04:002013-05-12T22:26:05.188-04:00If I Could Change PerlIs there something that irritates you about Perl? One little thing you wish you could change, to make life so much easier?<br />
<br />
For me, it's the way declarations work. Whether it's with local, our or my, you can declare a variable name, or a list of several variable names:<br />
<br />
<pre><span style="font-family: 'Courier New', Courier, monospace;">my ($x, $y, $z);</span></pre>
Of course, you can initaliaze variables as you declare them.
<br />
<pre><span style="font-family: Courier New, Courier, monospace;">my $bank_balance = -999_999;
my ( $x, $y, $z ) = ( 0, 0, 0 );</span>
</pre>
<br />
But if you have a number of variables to declare, and they aren't directly related to each other, as (x, y, z) clearly are, it would be so much better to declare the variable and immediately assign a value to it on the same line, the way C, Javascript and numerous sensible langages do.<br />
<br />
Currently, 'my', 'our' and 'local' expect a variable name, or a list of mariable names. So one possibility would be to provide an alternative form which takes a hash. Ideally, values defined in one line could be used lower down.<br />
<pre><span style="font-family: Courier New, Courier, monospace;">
my {
$sides => 3,
$lengths => [3, 4, 5],
};
</span></pre>
I suppose people will ignore this article or come out with an explanation of why I'm wrong.
<br />
All the same, what little feature would make your <b>Perl</b> day better?
<br />
Tom Legradyhttp://www.blogger.com/profile/12348339971676149630noreply@blogger.com5tag:blogger.com,1999:blog-2179901611970593232.post-22863968122883518642013-04-21T19:45:00.000-04:002017-09-28T15:47:09.467-04:00Learning Perl5i #3 revistedI was somewhat uncomfortable that my solution to the combinatorics task, which involved a single, not very long routine, became so long with the addition of command line processing and POD documentation. For one thing, while it had some information about how to invoke the routine, it did not have tests.<br />
<br />
I still feel that a book on programming, say, should have a fully-implemented solution once a chapter, perhaps, to show the way things should be done. But that is too long for every single bit of code. Full documentation and sample implementations stretch on too long, compared to the few lines a snippet actually deserves.<br />
<br />
Providing tests may still be appropriate. They take up less space than full documentation, yet explain how the various components work, by the very act of stress-testing those lines of code. Tests provide examples of how the code should be invoked.<br />
<br />
Revising the combinatorics task into a module, this generates:<br />
<br />
<span style="font-family: Courier New, Courier, monospace;">use perl5i::2;</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"># ----------------------------------------</span><br />
<span style="font-family: Courier New, Courier, monospace;"># generate combinations of length $n consisting of characters</span><br />
<span style="font-family: Courier New, Courier, monospace;"># from the sorted set @set, using each character once in a</span><br />
<span style="font-family: Courier New, Courier, monospace;"># combination, with sorted strings in sorted order.</span><br />
<span style="font-family: Courier New, Courier, monospace;">#</span><br />
<span style="font-family: Courier New, Courier, monospace;"># Returns a list of array references, each containing one </span><br />
<span style="font-family: Courier New, Courier, monospace;"># combination.</span><br />
<span style="font-family: Courier New, Courier, monospace;">#</span><br />
<span style="font-family: Courier New, Courier, monospace;">func combine($n, @set) {</span><br />
<span style="font-family: Courier New, Courier, monospace;"> return unless @set;</span><br />
<span style="font-family: Courier New, Courier, monospace;"> return map { [ $_ ] } @set if $n == 1;</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"> my ($head) = shift @set;</span><br />
<span style="font-family: Courier New, Courier, monospace;"> my @result = combine( $n-1, @set );</span><br />
<span style="font-family: Courier New, Courier, monospace;"> for my $subarray ( @result ) {</span><br />
<span style="font-family: Courier New, Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>$subarray->unshift( $head );</span><br />
<span style="font-family: Courier New, Courier, monospace;"> }</span><br />
<span style="font-family: Courier New, Courier, monospace;"> return ( @result, combine( $n, @set ) );</span><br />
<span style="font-family: Courier New, Courier, monospace;">}</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"># ------------------------------------------------------------------</span><br />
<span style="font-family: Courier New, Courier, monospace;"># Tests</span><br />
<span style="font-family: Courier New, Courier, monospace;">#</span><br />
<span style="font-family: Courier New, Courier, monospace;">func runtests() {</span><br />
<span style="font-family: Courier New, Courier, monospace;"> eval { use Test::More };<span class="Apple-tab-span" style="white-space: pre;"> </span># only include if testing</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"> test_no_set();</span><br />
<span style="font-family: Courier New, Courier, monospace;"> test_length_zero();</span><br />
<span style="font-family: Courier New, Courier, monospace;"> test_length_one();</span><br />
<span style="font-family: Courier New, Courier, monospace;"> test_longer();</span><br />
<span style="font-family: Courier New, Courier, monospace;"> done_testing();</span><br />
<span style="font-family: Courier New, Courier, monospace;">}</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">func test_no_set() {</span><br />
<span style="font-family: Courier New, Courier, monospace;"> is( combine( 1, () ), undef, </span><br />
<span style="font-family: Courier New, Courier, monospace;"> 'set == empty list returns empty array' );</span><br />
<span style="font-family: Courier New, Courier, monospace;"> is( combine( 1 ), undef, </span><br />
<span style="font-family: Courier New, Courier, monospace;"> 'set not provided returns empty array' );</span><br />
<span style="font-family: Courier New, Courier, monospace;">}</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">func test_length_zero() {</span><br />
<span style="font-family: Courier New, Courier, monospace;"> is( combine( 0, 'a' ), undef, 'zero length returns empty array' );</span><br />
<span style="font-family: Courier New, Courier, monospace;">}</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">func test_length_one() {</span><br />
<span style="font-family: Courier New, Courier, monospace;"> my ( @results ) = combine( 1, 'a' );</span><br />
<span style="font-family: Courier New, Courier, monospace;"> is_deeply( \@results, [['a']],</span><br />
<span style="font-family: Courier New, Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span> 'length 1 returns unary set as array element');</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"> @results = combine( 1, 'a', 'b', 'c' );</span><br />
<span style="font-family: Courier New, Courier, monospace;"> is_deeply( \@results, [['a'], ['b'], ['c']],</span><br />
<span style="font-family: Courier New, Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span> 'length 1 returns larger set as array elements');</span><br />
<span style="font-family: Courier New, Courier, monospace;">}</span><br />
<span style="font-family: Courier New, Courier, monospace;">func test_longer() {</span><br />
<span style="font-family: Courier New, Courier, monospace;"> my ( @results ) = combine( 2, 'a', 'b', 'c' );</span><br />
<span style="font-family: Courier New, Courier, monospace;"> is_deeply( \@results, [['a', 'b'], ['a', 'c'], ['b', 'c']],</span><br />
<span style="font-family: Courier New, Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span> 'longer length generates list of combinations');</span><br />
<span style="font-family: Courier New, Courier, monospace;">}</span><br />
<span style="font-family: Courier New, Courier, monospace;"># ------------------------------------------------------------------</span><br />
<span style="font-family: Courier New, Courier, monospace;"># Run tests if invoked as a program, rather than in cluded as a </span><br />
<span style="font-family: Courier New, Courier, monospace;"># module.</span><br />
<span style="font-family: Courier New, Courier, monospace;">#</span><br />
<span style="font-family: Courier New, Courier, monospace;">runtests() unless caller();</span><br />
<br />
Output from invoking the module as a program:<br />
<br />
<span style="font-family: Courier New, Courier, monospace;">bash-3.2$ perl combinations.pli</span><br />
<span style="font-family: Courier New, Courier, monospace;">ok 1 - set == empty list returns empty array</span><br />
<span style="font-family: Courier New, Courier, monospace;">ok 2 - set not provided returns empty array</span><br />
<span style="font-family: Courier New, Courier, monospace;">ok 3 - zero length returns empty array</span><br />
<span style="font-family: Courier New, Courier, monospace;">ok 4 - length 1 returns unary set as array element</span><br />
<span style="font-family: Courier New, Courier, monospace;">ok 5 - length 1 returns larger set as array elements</span><br />
<span style="font-family: Courier New, Courier, monospace;">ok 6 - longer length generates list of combinations</span><br />
<span style="font-family: Courier New, Courier, monospace;">1..6</span><br />
<br />Tom Legradyhttp://www.blogger.com/profile/12348339971676149630noreply@blogger.com0tag:blogger.com,1999:blog-2179901611970593232.post-87314448347749935312013-04-18T11:14:00.000-04:002017-09-28T15:47:09.533-04:00Learning Perl5i #3I've long admired the way functional languages specify multiple implementations of a routine for different sets of argument values, to detect terminal conditions. Converting a Perl6 solution of a <a href="http://rosettacode.org/wiki/Combinations">combinatorics task</a> to Perl5i, I realized the <a href="http://rosettacode.org/wiki/Combinations#Perl5i">plain Perl solution</a> isn't necessarily all that bad, either.<br />
<br />
The problem is to list the different ways you can select M elements from a set of N. Select 2 elements from a set of 4 would lead to the values ( 1, 2 ); ( 1, 3 ); ( 1, 4 ); ( 2, 3 ); ( 2, 4 ); ( 3, 4 ). Oh yeah, they want the characters in each combination in sorted order, and the list of combinations sorted, too.<br />
<br />
So it all consists of a single routine which gets invoked as <span style="font-family: Courier New, Courier, monospace;">combine(3, ['a'..'e'])</span>.<br />
<br />
Ignore the first couple of lines, for a moment. If the first character of the set exists in a combination, it must be in the first position, since the characters are in sorted order. That means the remainder of the combination must be one character shorter, made of the remaining characters. So that part calls for a recursive solution:<br />
<br />
* pull off the first character from the alphabet;<br />
* recursively determine combinations of length -1 from the remainder of the set;<br />
* reassemble be prefixing the first character onto each solution.<br />
<br />
The other possibility is that the first character is not in the solution. That calls for recursively invoking the routine with the original length, but a smaller alphabet. If we do that often enough, we reach a situation where the alphabet is empty. This situation is handled by the first line we skipped over. In fact, we could optimize and abort if the size of the alphabet is smaller than the length of string we are requesting.<br />
<br />
The more normal end case is when we are asking for a single character, the last in the sequence. In this situation, each character in the set is a candidate, so we return each character as an element in an anonymous array. Returning to the calling level, we then prefix the previous <i>head</i> of the alphabet onto the sequence using <span style="font-family: Courier New, Courier, monospace;">unshift</span>. When we reach the top level, we have a list of arrays, each array being one combination.<br />
<br />
<hr />
While I was satisfied with the solution, I was unhappy with hard-coding the invocation. If I'm trying to demonstrate good coding, I should allow the program to be invoked with command-line arguments, to vary the way it is used.<br />
<div>
<br /></div>
<div>
So I added my favourite command-line options module, Getopt::Long, and modified the main section to fetch and use options:</div>
<div>
<br /></div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace;">my $options = parse_options();</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">say @$_</span><span style="font-family: 'Courier New', Courier, monospace;"> for combine( $options->{length}, </span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> @{$options->{alphabet}} );</span></div>
</div>
<div>
<br /></div>
<div>
The routine to read options looks like this:</div>
<div>
<br /></div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace;">func parse_options() {</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> my %options = ( length => 3,</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span> set => 5,</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span> );</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> GetOptions( \%options, 'length=s', 'set=s' )</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>or die( 'some usage message';</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> $options{alphabet} = </span><br />
<span style="font-family: Courier New, Courier, monospace;"> [ ('a'..'z')[0..$options{set}-1] ];</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> return \%options;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">}</span></div>
</div>
<div>
<br /></div>
<div>
Getopt::Long has a rich set of options to support single character or long argument names, to allow flags, options with string or numeric values, hashes and even multiple values, as well as supporting multiple names and abbreviations. You can specify a parameter, and then specify a reference to a scalar in which to store the value, but I prefer to pass a reference to a hash as the first argument, which gets all the values.</div>
<div>
<br /></div>
<div>
By setting some values in the hash, you can specify default values.</div>
<div>
<br /></div>
<div>
In this case I allow two arguments, the length of the strings, <i>M</i> in combinatorics talk, and the size of the alphabet, <i>N</i> in combinatorics talk. I then create an alphabet of set N by taking an appropriately-sized subset of the sequence <i>a</i> to <i>z</i>.</div>
<div>
<br /></div>
<hr />
<br />
But if GetOptions fails because the argument list is totally unusable, you need to report an error. And it should probably provide a usage message, describing how to invoke the program. My favourite solution is Pod::Usage, which makes it easy to display the POD contained in the file ... but that means documenting the program with POD.<br />
<br />
So the question is, is this getting silly? Or is it an appropriate example to present to people wanting to learn modern Perl?<br />
<br />
<br />
<span style="font-family: Courier New, Courier, monospace;">use perl5i::2;</span><br />
<span style="font-family: Courier New, Courier, monospace;">use Getopt::Long;</span><br />
<span style="font-family: Courier New, Courier, monospace;">use Pod::Usage;</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"># ----------------------------------------</span><br />
<span style="font-family: Courier New, Courier, monospace;"># Generate combinations of length $len consisting</span><br />
<span style="font-family: Courier New, Courier, monospace;"># </span><span style="font-family: 'Courier New', Courier, monospace;">of characters </span><span style="font-family: 'Courier New', Courier, monospace;">from </span><span style="font-family: 'Courier New', Courier, monospace;">the sorted set @chars, using </span><br />
<span style="font-family: Courier New, Courier, monospace;"># </span><span style="font-family: 'Courier New', Courier, monospace;">each character once in a </span><span style="font-family: 'Courier New', Courier, monospace;">combination,</span><span style="font-family: 'Courier New', Courier, monospace;">with sorted</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"># strings in sorted order.</span><br />
<span style="font-family: Courier New, Courier, monospace;">#</span><br />
<span style="font-family: Courier New, Courier, monospace;"># We will use a recursive solution:</span><br />
<span style="font-family: Courier New, Courier, monospace;"># * If we run out of eligable characters, we've</span><br />
<span style="font-family: Courier New, Courier, monospace;"># </span><span style="font-family: 'Courier New', Courier, monospace;">gone too far, and </span><span style="font-family: 'Courier New', Courier, monospace;">won't find a solution along</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"># this path.</span><br />
<span style="font-family: Courier New, Courier, monospace;"># * If we are looking for a single character, each </span><br />
<span style="font-family: Courier New, Courier, monospace;"># </span><span style="font-family: 'Courier New', Courier, monospace;">character in </span><span style="font-family: 'Courier New', Courier, monospace;">@set is </span><span style="font-family: 'Courier New', Courier, monospace;">eligible, so return each</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"># </span><span style="font-family: 'Courier New', Courier, monospace;">as the single element of an </span><span style="font-family: 'Courier New', Courier, monospace;">array.</span><br />
<span style="font-family: Courier New, Courier, monospace;"># * We have not yet reached the last character, so</span><br />
<span style="font-family: Courier New, Courier, monospace;"># there are two </span><span style="font-family: 'Courier New', Courier, monospace;">possibilities:</span><br />
<span style="font-family: Courier New, Courier, monospace;"># 1) push the first element onto the front of an </span><br />
<span style="font-family: Courier New, Courier, monospace;"># </span><span style="font-family: 'Courier New', Courier, monospace;">N-1 length </span><span style="font-family: 'Courier New', Courier, monospace;">combination </span><span style="font-family: 'Courier New', Courier, monospace;">from the remainder of</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"># the set.</span><br />
<span style="font-family: Courier New, Courier, monospace;"># 2) skip the current element, and generate an </span><br />
<span style="font-family: Courier New, Courier, monospace;"># </span><span style="font-family: 'Courier New', Courier, monospace;">N-length </span><span style="font-family: 'Courier New', Courier, monospace;">combination </span><span style="font-family: 'Courier New', Courier, monospace;">from the remainder</span><br />
<span style="font-family: Courier New, Courier, monospace;">#</span><br />
<span style="font-family: Courier New, Courier, monospace;">func combine($len, @chars) {</span><br />
<span style="font-family: Courier New, Courier, monospace;"> return unless @chars;</span><br />
<span style="font-family: Courier New, Courier, monospace;"> return map { [ $_ ] } @chars if $len == 1;</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"> my ($head) = shift @chars;</span><br />
<span style="font-family: Courier New, Courier, monospace;"> my @result = combine( $len-1, @chars );</span><br />
<span style="font-family: Courier New, Courier, monospace;"> for my $subarray ( @result ) {</span><br />
<span style="font-family: Courier New, Courier, monospace;"> $subarray->unshift( $head );</span><br />
<span style="font-family: Courier New, Courier, monospace;"> }</span><br />
<span style="font-family: Courier New, Courier, monospace;"> return ( @result, combine( $len, @chars ) );</span><br />
<span style="font-family: Courier New, Courier, monospace;">}</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">func parse_options() {</span><br />
<span style="font-family: Courier New, Courier, monospace;"> my %options = ( length => 3,</span><br />
<span style="font-family: Courier New, Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span> set => 5,</span><br />
<span style="font-family: Courier New, Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span> );</span><br />
<span style="font-family: Courier New, Courier, monospace;"> GetOptions( \%options, 'length=s', 'set=s',</span><br />
<span style="font-family: Courier New, Courier, monospace;"> 'man', 'help' )</span><br />
<span style="font-family: Courier New, Courier, monospace;"><span class="Apple-tab-span" style="white-space: pre;"> </span>or pod2usage(2);</span><br />
<span style="font-family: Courier New, Courier, monospace;"> pod2usage(1) if $options{help};</span><br />
<span style="font-family: Courier New, Courier, monospace;"> pod2usage(-verbose => 2) if $options{man};</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"> $options{alphabet} =</span><br />
<span style="font-family: Courier New, Courier, monospace;"> [ ('a'..'z')[0..$options{set}-1] ];</span><br />
<span style="font-family: Courier New, Courier, monospace;"> return \%options;</span><br />
<span style="font-family: Courier New, Courier, monospace;">}</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">my $options = parse_options();</span><br />
<span style="font-family: Courier New, Courier, monospace;">say @$_</span><br />
<span style="font-family: Courier New, Courier, monospace;"> for combine( $options->{length},</span><br />
<span style="font-family: Courier New, Courier, monospace;"> @{$options->{alphabet}} );</span><br />
<span style="font-family: Courier New, Courier, monospace;">__END__</span><br />
<br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">=pod</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">=head1 NAME</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">combinations.pli - determine possible combinations of M elements from a set of N</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">=head1 VERSION</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">This documentation applies to combinations.pli version 0.0.1</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">=head1 SYNOPSYS</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"> combinations.pli [--length M] [--set N] \</span><br />
<span style="font-family: Courier New, Courier, monospace;"> [-man] [-help]</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">=head1 OPTIONS</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">=over 4</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">=item length M</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">Create strings of this length</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">=item set N</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">Use an alphabet of this many characters</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">=item help</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">Display a brief summary of information about the program.</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">=item man</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">Display the full documentation.</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">=back</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">=head1 DESCRIPTION</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">Generate combinations of length C<length><length><length> consisting of characters from </length></length></span><span style="font-family: 'Courier New', Courier, monospace;">the sorted set C</span><span style="font-family: 'Courier New', Courier, monospace;"><alphabet></span><span style="font-family: 'Courier New', Courier, monospace;"><alphabet><alphabet>, using each character once in a combination, </alphabet></alphabet></span><span style="font-family: 'Courier New', Courier, monospace;">with sorted strings in sorted order.</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">=cut</span><br />
<br />Tom Legradyhttp://www.blogger.com/profile/12348339971676149630noreply@blogger.com0tag:blogger.com,1999:blog-2179901611970593232.post-87092575669344048112013-04-16T13:40:00.003-04:002017-09-28T15:47:09.528-04:00Learning Perl5i #2Continuing the exploration of Perl5i beyond <a href="http://which-dwarf-are-you.blogspot.ca/2013/03/learning-perl5i.html" target="_blank">part one</a>, I decided to treat <i>perl5i</i> as a language and use it to implement tasks at <a href="http://rosettacode.org/">Rosetta Code</a>.<br />
<br />
Since Moose is a complete OO solution, I use a lightweight approach to OO, where used.<br />
<br />
The first RC task I implemented is <a href="http://rosettacode.org/wiki/100_doors">100 doors</a>: see <a href="http://rosettacode.org/wiki/100_doors#Perl5i">the full implementation.</a><br />
<br />
In terms of documentation, I gave each routine a small header in which I show how the routine is invoked, specify the inputs and outputs, and summarize the primary actions of the routine.<br />
<br />
Since the task is defined in terms of a number of doors, and operations on those doors, it seemed appropriate to use an object. "some object" + "operations on that object" ==> "segregate into an OO implementation".<br />
<br />
I used a simple constructor with initialization performed in <span style="font-family: Courier New, Courier, monospace;">_init()</span> to simplify subclassing. The constructor doesn't need to be modified, just a new <span style="font-family: Courier New, Courier, monospace;">_init()</span> written to handle additional attributes or arguments, if any. The new implementation can handle the duties of the original routine, or better, invoke the original as <span style="font-family: Courier New, Courier, monospace;">$self->SUPER::_init( @_ )</span>.<br />
<br />
The initializer stores how many doors we are dealing with, then then creates an array of doors. We are interested in doors 1..N, but we actually create one more, 0..N, since that's the way Perl arrays work. We'll simply ignore door zero, it is permanently closed. One subtle detail about the <span style="font-family: Courier New, Courier, monospace;">x</span> multiplier: <span style="font-family: Courier New, Courier, monospace;">$CLOSED x $N</span> is 0000..., string repetition; list repetition uses parentheses: (<span style="font-family: Courier New, Courier, monospace;">$CLOSED) x $N</span> to generate ( 0, 0, 0, ... ).<br />
<br />
The instructions say there are N passes, in which you toggle every door, every other door, every Nth door. I created <span style="font-family: Courier New, Courier, monospace;">toggle()</span> to toggle a single door, <span style="font-family: Courier New, Courier, monospace;">toggle_n()</span> to process a single pass, and <span style="font-family: Courier New, Courier, monospace;">toggle_all()</span> to handle all the passes.<br />
<br />
Besides eliminating duplication and excessive typing, one advantage of the ternary operator ( ? : ) over an if-block is that it is clear a single l-value is the target.<br />
<br />
Simple statement modifier loops like these are, I believe, quicker than more general loops, and they offer simpler optimization in the future into parallel processing, but they also communicate that we are dealing with a set of data. Too often, people coming to Perl from C and Java think in terms of indices: <i>I get the first index; I get the first element in the array; I do something</i> ... I prefer to think in terms of sets, like a Unix pipeline: <i><span style="font-family: Times, Times New Roman, serif;">Take this array of elements, do something to them</span>,</i> ...<br />
<br />
Note in <span style="font-family: Courier New, Courier, monospace;">toggle_n()</span>, that <span style="font-family: Courier New, Courier, monospace;">$n * int( N / $n )</span> is the largest multiple of <span style="font-family: Courier New, Courier, monospace;">$n</span> les than N. Taking the sequence <span style="font-family: Courier New, Courier, monospace;">1.. int($self->{N} / $n )</span> and multiplying each element by <span style="font-family: Courier New, Courier, monospace;">$n</span> produces <span style="font-family: Courier New, Courier, monospace;">$n, 2 * $n, 3 * $n, ... $n * int( N / $n )</span>, the list of doors to toggle on that pass.
<br />
<br />
The final requirement is to print which doors are open. <span style="font-family: Courier New, Courier, monospace;">grep()</span> takes a code block and a list of values. Values are passed if they generate a true value in the block, and barred if they generate a false value.
<br />
<br />
Putting the routines together into a demonstration of the 100 door task becomes a simple three-line sequence.Tom Legradyhttp://www.blogger.com/profile/12348339971676149630noreply@blogger.com1tag:blogger.com,1999:blog-2179901611970593232.post-80899554520040851532013-03-23T08:26:00.003-04:002017-09-28T15:47:09.518-04:00Learning Perl5iI've been looking at the module <span style="font-family: Courier New, Courier, monospace;">perl5i</span>, which puts a sideways twist on Perl, eliminating tons of boring boilerplate, and introduce many conveniences. In this case, the <span style="font-family: Courier New, Courier, monospace;">i</span> eliminates complexity.<br />
<br />
Installing <span style="font-family: Courier New, Courier, monospace;">perl5i</span> installs a number of other modules as well, on the other hand life becomes better once they are installed.<br />
<br />
If you don't have <span style="font-family: Courier New, Courier, monospace;">cpanm</span> or one of the other improved CPAN installers, the simplest way is to install perl5i and its friends using the command<br />
<br />
<span style="font-family: Courier New, Courier, monospace;">sudo cpan perl5i</span><br />
<br />
You may need to type in <span style="font-family: Courier New, Courier, monospace;">yes</span> to have <span style="font-family: Courier New, Courier, monospace;">cpan</span> install dependencies. Some modules are needed only temporarily during installation, and you are asked whether you want them permanently installed. Perl modules don't take up very much space, so I always go for it. Installation is a bit different on Windows or Linux machines, but you can figure it out, you're a mighty Perl programmer!<br />
<br />
One consideration is that major versions of <span style="font-family: Courier New, Courier, monospace;">perl5i</span> may change in drastic ways. But you don't include <span style="font-family: Courier New, Courier, monospace;">perl5i</span>; what you specify is <span style="font-family: Courier New, Courier, monospace;">perl5i::2</span>. The newest module includes <span style="font-family: Courier New, Courier, monospace;">perl5i::0</span> and <span style="font-family: Courier New, Courier, monospace;">perl5i::1</span> as well, so old code is supported until you are ready to upgrade them.<br />
<br />
The immediate benefit is that you no longer have to enable strictness. <span style="font-family: Courier New, Courier, monospace;">perl5i</span> automatically provides<br />
<br />
<span style="font-family: Courier New, Courier, monospace;">use warnings;</span><br />
<span style="font-family: Courier New, Courier, monospace;"> use strict;</span><br />
<br />
So it costs you 14 characters to type<br />
<br />
<span style="font-family: Courier New, Courier, monospace;"> use perl5i::2;</span><br />
<br />
and straight off the bat it saves you 24 characters ....profit!<br />
<br />
<br />
Oh, and it automatically activates features <span style="font-family: Courier New, Courier, monospace;">say</span>, <span style="font-family: Courier New, Courier, monospace;">state</span> and <span style="font-family: Courier New, Courier, monospace;">switch</span>.<br />
<br />
<br />
But wait! There's more! Not only does it slice and dice, it also julienes!<br />
<br />
Well, no cutting anything in any shape ... but <span style="font-family: Courier New, Courier, monospace;">perl5i</span> DOES provide <span style="font-family: Courier New, Courier, monospace;">autodie</span>. If your <span style="font-family: Courier New, Courier, monospace;">open()</span> doesn't work, the program dies; if the<span style="font-family: Courier New, Courier, monospace;"> close()</span> doesn't work, the program dies; if <span style="font-family: Courier New, Courier, monospace;">print()</span> or a <span style="font-family: Courier New, Courier, monospace;">system()</span> call or <span style="font-family: Courier New, Courier, monospace;">unlink()</span> don't work, the program dies with an exception that turns into a string if you don't handle it.<br />
<br />
Having a program die is an effective way to trigger in-house failure detection systems, such as Nagios. So simply doing the things you want, without checking return values, becomes a viable way to write a secure program that detects problems. If you need to catch and handle problems, perl5i provides <span style="font-family: Courier New, Courier, monospace;">Try::Tiny</span>. There are other attempts at exception handling; any of them would be clearer than using <span style="font-family: Courier New, Courier, monospace;">eval</span>. Try::Tiny is small, it does the job, and the intent is clear.<br />
<br />
<span style="font-family: Courier New, Courier, monospace;"> try {</span><br />
<span style="font-family: Courier New, Courier, monospace;"> open my $h, '<', '/does/not/exist';</span><br />
<span style="font-family: Courier New, Courier, monospace;"> } </span><br />
<span style="font-family: Courier New, Courier, monospace;"> catch {</span><br />
<span style="font-family: Courier New, Courier, monospace;"> say "the open() died with message $_";</span><br />
<span style="font-family: Courier New, Courier, monospace;"> };</span><br />
<br />
You can also add a <span style="font-family: Courier New, Courier, monospace;">finally</span> block, to perform things that need to happen regardless of success or failure. The single weakness is that <span style="font-family: Courier New, Courier, monospace;">try</span> is a statement, not a block, so it needs a semi-colon<br />
<br />
So that's a major from installing perl5i, and we've barely gotten started.Tom Legradyhttp://www.blogger.com/profile/12348339971676149630noreply@blogger.com0