For example the following function:
fun fac (0 : int) : int = 1
| fac (n : int) : int = n * fac (n - 1)
Or in the function:
fun even 0 = true
| even x = odd(x-1)
and odd 0 = false
| odd x = even(x-1);
I have little experience with ML and I'm just trying to understand the basics.
A pipe simply refers to a temporary software connection between two programs or commands. An area of the main memory is treated like a virtual file to temporarily hold data and pass it from one process to another in a single direction. In OSes like Unix, a pipe passes the output of one process to another process.
The pipe operator, written as %>% , has been a longstanding feature of the magrittr package for R. It takes the output of one function and passes it into another function as an argument. This allows us to link a sequence of analysis steps.
What is the pipe operation? Pipe is a module in Python that easily connects the output from one method with the output from another method. It is a library that helps in writing cleaner code.
A pipe is a system call that creates a unidirectional communication link between two file descriptors. The pipe system call is called with a pointer to an array of two integers. Upon return, the first element of the array contains the file descriptor that corresponds to the output of the pipe (stuff to be read).
This is pattern matching. From the link:
Functions may be composed of one or more rules. A rule consists of its function-name, an argument pattern and its expression. When the function is called the argument values will be matched to the patterns in top down order. Functions using pattern-matching are very similar to case expressions
This means the pipe separates cases for pattern matching. Pattern matching matches special patterns and executes specific expressions based on that pattern. They follow the syntax:
fun <fun-name> <pattern>
| <fun-name> <pattern>
| <fun-name> <pattern>;
Where <pattern>
is:
<args> = <expressions>
In your first example, it is declaring function fac
for factorial calculation. The first pattern is when the argument, int
is 0. If int
is 0, the expression of that case will execute, and in this case if the passed argument is 0, the result will be 1 (because 0 factorial is 1). If the passed argument is not 0 then it follows the next pattern (as it did not match the first pattern). If the passed argument is, say 2, it will perform recursion and find the factorial accordingly.
Consider the snippet below:
fun past "run" = "ran"
| past "swim" = "swam"
| past x = x ^ "ed";
We define function named past
that takes an argument and finds the passed tense of the argument. The first pattern is equivalent to - in plain English:
if the word (or passed argument) is "run", the passed tense is "ran".
The second pattern is equivalent to:
if the word (or passed argument) is "swim", the passed tense is "swam".
If the word is neither "swim" or "run" (and thus the two patterns are not matched) continue with the last pattern, which just adds "ed" to the end of the word.
With the example you can see that |
(pipe) symbols separate patterns. You can think of patterns like this pseudocode:
if argument is "run", then passed tense is "ran"
else if argument is "swim", then passed tense is "swam"
else add "ed" to end of word
Pattern matching is used to cover irregular cases. Since "ran" and "swam" are irregular passed tenses, we cover those cases with patterns. The exact same principle applies to the first example - 0! is a special case, it is 1. Because of this, we can use pattern matching to match the specific case where the argument is 0 and execute according to that specific case.
What you are seeing is the shortcut syntax for defining a function and pattern matching on the argument at the same time. If you split out the pattern matching from the function definition, it would look something like this:
fun fac (x : int) : int =
case x of
0 => 1
| n => n * fac (n - 1)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With