官术网_书友最值得收藏!

Invariable variables and pattern matching

One of the most misunderstood concepts in functional programming is that of assignment. Or, said another way, assignment doesn't exist.

Let's try to dispel this misconceived idea. In iex, we might see some code like this:

iex(1)> a = 2
2
iex(2)> a + 4
6

We may be tempted to explain the preceding code snippet with something like, "So we assign 2 to a and then add 4 to a giving us 6." However, in Elixir, this is incorrect. Elixir does not define = as an assignment operator, but rather a match operator. That is, Elixir attempts to match the left side of the = operator to that of the right.

In step 1, for Elixir to make the match succeed, we bind the value of 2 to the variable, a. Then later, when we perform the addition, we are substituting 2 for a, yielding an expression that looks like 2 + 4, which obviously equals 6.

This is a really different way to think about what is going on internally. Take a moment to let it sink in.

Back? Ready to move forward? Good!

If all we are doing is binding, why can we do something like this:

iex(1)> a = 2
2
iex(2)> a = 3
3

This is because Elixir will rebind values to make the match succeed. Notice, however, you are not able to do this:

iex(1)> a = 2
2
iex(2)> 3 = a
** (MatchError) no match of right hand side value: 2

But we can do something like this:

iex(1)> a = 2
2
iex(2)> 2 = a
2

This is the basis of what functional languages call pattern matching and is really one of the fundamental things that makes functional programming so different and so exciting.

Since Elixir will always try to make the left-hand side match the right, we can use this to our advantage to decompose lists, extract elements, or decompose complex structures into simpler variables.

Let's take a look at decomposing lists.

We may define a list as:

iex(1)> list = [1, 2, 3]
[1, 2, 3]

Then, we may attempt to match it against a, b, c:

iex(2)> [a, b, c] = list
[1, 2, 3]
iex(3)> a
1
iex(4)> b
2
iex(5)> c
3

So Elixir attempts to create a match between the left side: [a, b, c] and list (which evaluates to [1, 2, 3]). For Elixir to successfully match these two sides, a, b, and c must be bound to the values 1, 2, and 3, respectively. As we see in commands 3-5, this is indeed the case.

Similarly, literal values can be used in the match expressions, using the same list:

iex(2)> [a, 2, c] = list
[1, 2, 3]
iex(3)> a
1
iex(4)> c
3

Using the underscore

In many functional languages, the underscore (_) is used to denote values we don't wish to bind to any value, or we don't care to use. Using the previous example, perhaps we don't care about the middle value of the list, but we can use the underscore to match against the 2, or whatever it actually is:

iex(2)> [a, _, c] = list
[1, 2, 3]
iex(3)> a
1
iex(4)> c
3
iex(5)> _
** (CompileError) iex:5: unbound variable _

It turns out that the underscore is a bit of a special variable. It allows us to match any expression, for example, _ = list would match successfully, but Elixir never keeps the bound value. This is good for two reasons. Firstly, semantically speaking, when reading any Elixir code, if we see the underscore, we know we can ignore it, and, secondly, the fact that Elixir will not allow us to use it will certainly help ensure that we don't make any mistakes with its actual value.

More pattern matching

Pattern matching has all sorts of uses in Elixir. Let's consider binary data deconstruction using pattern matching. While we're at it, let's learn about IEEE-754, the standard that defines how computers handle floating-point numbers in memory.

IEEE-754

If you're familiar with the IEEE-754 standard, feel free to skip a few paragraphs ahead.

For the sake of the discussion, we will assume we are talking about 64-bit or double precision floating-point numbers. There are other representations, 32-bit/single and 128-bit/quadruple precision, but they usually only differ by the number of bits available.

The IEEE-754 standard, first introduced in 1985, defines a couple of terms we need to become familiar with for our discussion. There is the sign, the exponent, the fraction or mantissa, and the bias.

The sign is denoted by the most significant bit. The exponent is given 11 bits, and the fraction is given 52 bits. The bias is just a constant for the exponent value.

With 11 bits for the exponent, we can store numbers between -1024 to 1023, or without the sign, 0 to 2047. We use the bias to keep the sign bit out of the number; however, we don't gain more precision here, this just has to do with the arithmetic involved. For example, to store a 0 exponent, we would store 1023 or 01111111111.

The fraction or mantissa is given 52 bits. This means that we can represent the fractional bit of a floating point with 52 bits of precision. This gives us a lot of space to represent numbers. However, we can usually give ourselves a little more by assuming that the first bit is, implicitly, a leading 1.

Putting it all together, using the values of each set of bits, denoting integers, we can compute the float saved in memory:

sign(1 + mantissa/pow(2,52)) * pow(2,exp ? 1023)

Or, perhaps a better way to see how IEEE-754 works is to use Elixir to help us match the bits and rebuild it with the preceding equation:

iex(1)> <<sign::size(1), exp::size(11),mantissa::size(52)>> =
<<3.14159::float>>
<<64, 9, 33, 249, 240, 27, 134, 110>>
iex(2)> sign
0
iex(3)> exp
1024
iex(4)> mantissa
2570632149304942
iex(5)> (1 + mantissa / :math.pow(2, 52)) * :math.pow(2, exp-1023)
3.14159

We don't include the sign bit because we already know it's zero and the number is positive.

There are lots of reasons to learn more about IEEE-754, especially if you're computing lots of numerical results. And maybe you won't use Elixir to do those computations, but using Elixir's pattern matching on binaries is a great way to decompose and debug why using 1-tanx is a bad idea when x has values near π?4 and 5π?4.

主站蜘蛛池模板: 三河市| 鄱阳县| 浙江省| 富民县| 定南县| 页游| 咸丰县| 灵石县| 泗水县| 紫阳县| 南涧| 正镶白旗| 南江县| 夹江县| 达孜县| 南漳县| 慈利县| 南召县| 古田县| 襄汾县| 碌曲县| 宝鸡市| 保靖县| 于都县| 高邑县| 汽车| 彰化县| 威信县| 南安市| 丹凤县| 青海省| 敦化市| 琼海市| 班戈县| 潞城市| 汉寿县| 麦盖提县| 崇阳县| 玉龙| 肥城市| 湛江市|