From 0f511256bb53abb482d6e7db6ba3c8524035a613 Mon Sep 17 00:00:00 2001 From: Anna Date: Wed, 13 Mar 2019 15:06:18 +0300 Subject: [PATCH 1/3] Edited stack-based-calculator.md until line 357 --- posts/stack-based-calculator.md | 88 ++++++++++++++++----------------- 1 file changed, 43 insertions(+), 45 deletions(-) diff --git a/posts/stack-based-calculator.md b/posts/stack-based-calculator.md index 3326305..41498c9 100644 --- a/posts/stack-based-calculator.md +++ b/posts/stack-based-calculator.md @@ -10,11 +10,11 @@ categories: [Combinators, Functions, Worked Examples] > In this post, we'll implement a simple stack based calculator (also known as "reverse Polish" style). The implementation is almost entirely done with functions, with only one special type and no pattern matching at all, so it is a great testing ground for the concepts introduced in this series. -В данной статье мы реализуем простой основанный на стеке калькулятор (также известный как "обратный польский" стиль). Реализация почти полностью построена на функциях, лишь с одним специальным типом и без сопоставления шаблонов вообще, это превосходный полигон для концепций затронутых в данной серии. +В этой статье мы реализуем простой стековый калькулятор (также известный как "обратная Польская нотация"). Реализация практически полностью построена на функциях, лишь с одним специальным типом, и вообще без сопоставления с образцом, так что это превосходный полигон для концепций, затронутых в нашей серии. > If you are not familiar with a stack based calculator, it works as follows: numbers are pushed on a stack, and operations such as addition and multiplication pop numbers off the stack and push the result back on. -Если вы незнакомы с подобным калькулятором, то он работает следующим образом: числа помещаются в стек, а операции, такие как сложение и произведение числа забирают числа с вершины стека, после чего помещают обратно полученный результат операции. +Если вы не знакомы с подобным калькулятором, то он работает следующим образом: числа помещаются в стек, а операции, такие как сложение и умножение, забирают числа с вершины стека, после чего помещают обратно полученный результат операции. > Here is a diagram showing a simple calculation using a stack: @@ -24,7 +24,7 @@ categories: [Combinators, Functions, Worked Examples] > The first steps to designing a system like this is to think about how it would be used. Following a Forth like syntax, we will give each action a label, so that the example above might want to be written something like: -Прежде чем проектировать подобную систему, следует порассуждать над тем, как она будет использоваться. Следуя подобному Forth синтаксису, дадим каждому действию соответствующую метку, чтобы в приведенном выше примере можно было написать нечто вроде: +Прежде чем проектировать подобную систему, следует порассуждать над тем, как она будет использоваться. Следуя Forth-подобному синтаксису, дадим каждому действию соответствующую метку, чтобы в приведенном выше примере можно было написать нечто вроде: EMPTY ONE THREE ADD TWO MUL SHOW @@ -36,7 +36,7 @@ categories: [Combinators, Functions, Worked Examples] > First we need to define the data structure for a stack. To keep things simple, we'll just use a list of floats. -Во первых надо определить структуру данных для стека. Это просто, для этих целей можно использовать список чисел с плавающей точкой. +Во первых, нужно определить структуру данных для стека. Для простоты можно использовать список чисел с плавающей точкой. ```fsharp type Stack = float list @@ -44,7 +44,7 @@ type Stack = float list > But, hold on, let's wrap it in a [single case union type](../posts/discriminated-unions.md#single-case) to make it more descriptive, like this: -Но лучше обернуть его в [single case union type](../posts/discriminated-unions.md#single-case) чтобы сделать его более наглядным, например так: +Но лучше обернуть его в [single case union type](../posts/discriminated-unions.md#single-case), чтобы сделать тип более наглядным, например так: ```fsharp type Stack = StackContents of float list @@ -56,7 +56,7 @@ type Stack = StackContents of float list > Now, to create a new stack, we use `StackContents` as a constructor: -Теперь создадим новый стек, используем `StackContents` в качестве конструктора: +Теперь создадим новый стек, используя `StackContents` в качестве конструктора: ```fsharp let newStack = StackContents [1.0;2.0;3.0] @@ -64,12 +64,12 @@ let newStack = StackContents [1.0;2.0;3.0] > And to extract the contents of an existing Stack, we pattern match with `StackContents`: -Для извлечения содержимого из существующего Stack-а используется сопоставление с шаблоном `StackContents`: +Для извлечения содержимого из существующего Stack-а используется сопоставление с образцом `StackContents`: ```fsharp let (StackContents contents) = newStack -// "contents" value set to +// Значение "contents" будет равно // float list = [1.0; 2.0; 3.0] ``` @@ -78,7 +78,7 @@ let (StackContents contents) = newStack > Next we need a way to push numbers on to the stack. This will be simply be prepending the new value at the front of the list using the "`::`" operator. -Далее необходим способ помещать числа в данный стек. Для этого достаточно добавить новое значение спереди списка используя "`::`". +Далее нам потребуется способ помещать числа в этот стек. Для этого достаточно добавить новое значение в начало списка, используя "`::`". > Here is our push function: @@ -93,11 +93,11 @@ let push x aStack = > This basic function has a number of things worth discussing. -Данная функция имеет ряд особенностей, которые стоит обсудить. +Эта функция имеет ряд особенностей, которые стоит обсудить. > First, note that the list structure is immutable, so the function must accept an existing stack and return a new stack. It cannot just alter the existing stack. In fact, all of the functions in this example will have a similar format like this: -Во первых, следует обратить внимание, что структура `list` неизменяемая, значит функция должна принимать существующий стек и возвращать новый. Это не просто изменение существующего стека. По факту, все функции в данном примере будут иметь подобный формат: +Во первых, следует обратить внимание на то, что структура `list` неизменяемая, значит функция должна принимать существующий стек и возвращать новый. Это не просто изменение существующего стека. По факту, все функции в данном примере будут иметь подобный формат: > Input: a Stack plus other parameters > Output: a new Stack @@ -107,11 +107,11 @@ let push x aStack = > Next, what should the order of the parameters be? Should the stack parameter come first or last? If you remember the discussion of [designing functions for partial application](../posts/partial-application), you will remember that the most changeable thing should come last. You'll see shortly that this guideline will be born out. -Во вторых, почему параметры идут именно в таком порядке? Почему стек должен идти первым или последним? В обсуждении [проектирование функций с частичным применением](../posts/partial-application) говорилось, что наиболее часто меняющийся параметр должен идти последним. Вскоре можно будет убедиться, что данные рекомендации соблюдаются. +Во вторых, почему параметры идут именно в таком порядке? Почему стек должен идти первым или последним? В разделе[проектирование функций с частичным применением](../posts/partial-application) говорилось, что наиболее часто меняющийся параметр должен идти последним. Вскоре можно будет убедиться, что эти рекомендации соблюдаются. > Finally, the function can be made more concise by using pattern matching in the function parameter itself, rather than using a `let` in the body of the function. -Наконец, функцию можно сделать более краткой с помощью сопоставления шаблонов в самом параметре функции, вместо `let` в теле функции. +Наконец, функцию можно сделать более краткой с помощью сопоставления с образцом в самом параметре функции, вместо `let` в теле функции. > Here is the rewritten version: @@ -128,7 +128,7 @@ let push x (StackContents contents) = > And by the way, look at the nice signature it has: -Между прочим, посмотрите на ее изящную сигнатуру: +Между прочим, посмотрите на её изящную сигнатуру: ```fsharp val push : float -> Stack -> Stack @@ -138,9 +138,9 @@ val push : float -> Stack -> Stack > In this case, I could probably guess what it did from the signature alone, even without knowing that the name of the function was "push". > This is one of the reasons why it is a good idea to have explicit type names. If the stack type had just been a list of floats, it wouldn't have been as self-documenting. -Как говорилось [ранее](../posts/function-signatures), сигнатура говорит нам, очень много о функции. -В данном случае, я мог бы догадаться, что делает данная функция лишь по ее сигнатуре, не зная, что она называется "push". -Это еще одна причина по которой было хорошей идеей иметь явные имена типа. Если бы стек был лишь списком чисел с плавающей точкой, то функция не была бы столь само-документированной. +Как говорилось [ранее](../posts/function-signatures), сигнатура сообщает нам очень многое. +В данном случае я мог бы догадаться, что делает данная функция, лишь по ее сигнатуре, даже не зная, что она называется "push". +Это еще одна причина по которой было хорошей идеей иметь явные имена типа. Если бы стек был лишь списком чисел с плавающей точкой, то функция не была бы столь самодокументированной. > Anyway, now let's test it: @@ -160,7 +160,7 @@ let stackWith2 = push 2.0 stackWith1 > With this simple function in place, we can easily define an operation that pushes a particular number onto the stack. -С помощью этой простой функции, можно легко определить операцию помещающую определенное число в стек. +С помощью этой простой функции можно легко определить операцию, помещающую определенное число в стек. ```fsharp let ONE stack = push 1.0 stack @@ -169,7 +169,7 @@ let TWO stack = push 2.0 stack > But wait a minute! Can you see that the `stack` parameter is used on both sides? In fact, we don't need to mention it at all. Instead we can skip the `stack` parameter and write the functions using partial application as follows: -Но подождите минуту! Разве не видите, что параметр `stack` используется с двух сторон? В действительно, совершенно необязательно упоминать его два раза. Вместо этого можно опустить параметр и написать функцию с частичным применением: +Но подождите минуту! Вы же видите, что параметр `stack` упоминается с обеих сторон выражения? В действительно, совершенно необязательно упоминать его два раза. Вместо этого можно опустить параметр и написать функцию с частичным применением: ```fsharp let ONE = push 1.0 @@ -181,11 +181,11 @@ let FIVE = push 5.0 > Now you can see that if the parameters for `push` were in a different order, we wouldn't have been able to do this. -Теперь очевидно, имей параметры `push` другой порядок, `stack` пришлось бы упоминать дважды. +Теперь очевидно, что если бы функция `push`имела другой порядок параметров, `stack` пришлось бы упоминать дважды. > While we're at it, let's define a function that creates an empty stack as well: -Стоит также определить функцию создающую пустой стек: +Стоит также определить функцию, создающую пустой стек: ```fsharp let EMPTY = StackContents [] @@ -219,24 +219,24 @@ let result312 = EMPTY |> THREE |> ONE |> TWO ``` -## Popping the stack | ~~Выталкивание стека~~ // TODO: Исправить +## Popping the stack | Выталкивание из стека // TODO: Исправить > That takes care of pushing onto the stack ? what about a `pop` function next? -С помещением в стек разобрались, но что насчет функции `pop`? +С добавлением в стек разобрались, но что насчет функции `pop`? > When we pop the stack, we will return the top of the stack, obviously, but is that all? -При извлечении из стека, очевидно необходимо вернуть вершину стека, но только ли ее? +При извлечении из стека, очевидно, необходимо вернуть вершину стека, но только ли ее? > In an object-oriented style, [the answer is yes](http://msdn.microsoft.com/en-us/library/system.collections.stack.pop.aspx). In an OO approach, we would *mutate* the stack itself behind the scenes, so that the top element was removed. -В объектно-ориентированном стиле, [ответ да](http://msdn.microsoft.com/en-us/library/system.collections.stack.pop.aspx). Но в случае ООП, стек был бы изменен за кулисами, так что верхний элемент был бы удален. +В объектно-ориентированном стиле [ответом будет "да"](http://msdn.microsoft.com/en-us/library/system.collections.stack.pop.aspx). Но в случае ООП, стек был бы изменен за кулисами, так что верхний элемент был бы удален. > But in a functional style, the stack is immutable. The only way to remove the top element is to create a *new stack* with the element removed. > In order for the caller to have access to this new diminished stack, it needs to be returned along with the top element itself. -Однако в функциональном стиле стек неизменяем. Есть только один способ удалить верхний элемент - создать _новый стек_ без удаленного элемента. Для того, чтобы вызывающий объект имел доступ к новому уменьшенному стеку, его необходимо вернуть вместе с верхним элементом. +Однако в функциональном стиле стек неизменяем. Есть только один способ удалить верхний элемент - создать _новый стек_ без этого элемента. Для того, чтобы вызывающий объект имел доступ к новому уменьшенному стеку, его необходимо вернуть вместе с верхним элементом. > In other words, the `pop` function will have to return *two* values, the top plus the new stack. The easiest way to do this in F# is just to use a tuple. @@ -247,8 +247,8 @@ let result312 = EMPTY |> THREE |> ONE |> TWO Реализация: ```fsharp -/// Pop a value from the stack and return it -/// and the new stack as a tuple +/// Вытолкнуть значение из стека +/// и вернуть его и новый стек в виде пары let pop (StackContents contents) = match contents with | top::rest -> @@ -275,8 +275,8 @@ let pop (StackContents contents) = > Try the code above and see what happens. You will get a compiler error! > The compiler has caught a case we have overlooked -- what happens if the stack is empty? -Но если попробовать выполнить коды выше, будет получена ошибка компиляции! -Компилятор обнаружил случай, который не был отработан, что произойдет, если стек пуст? +Попробуйте запустить этот код и посмотрите, что произойдёт. Вы получите ошибку компиляции! +Компилятор обнаружил случай, который не был отработан -- что произойдет, если стек пуст? > So now we have to decide how to handle this. @@ -290,11 +290,9 @@ let pop (StackContents contents) = > Generally, I prefer to use error cases, but in this case, we'll use an exception. So here's the `pop` code changed to handle the empty case: -Обычно, я предпочитаю использовать специализированные состояния ошибки, но в данном конкретном случае я предпочел использовать исключение. Исправленная версия `pop` с обработкой пустого случая: +Обычно я предпочитаю использовать специальное состояние для ошибки, но в данном конкретном случае я предпочел выбросить исключение. Исправленная версия `pop` с обработкой пустого случая: ```fsharp -/// Pop a value from the stack and return it -/// and the new stack as a tuple let pop (StackContents contents) = match contents with | top::rest -> @@ -322,29 +320,29 @@ let popped2, poppedStack2 = pop poppedStack let _ = pop EMPTY ``` -## Writing the math functions | Математические функции +## Writing the math functions | Арифметические функции > Now with both push and pop in place, we can work on the "add" and "multiply" functions: -Теперь когда добавление и удаление на месте, можно начать работу с функциями "add" и "mulytiply": +Теперь когда добавление и удаление на месте, можно начать работу с функциями "add" и "multiply": ```fsharp let ADD stack = - let x,s = pop stack //pop the top of the stack - let y,s2 = pop s //pop the result stack - let result = x + y //do the math - push result s2 //push back on the doubly-popped stack + let x,s = pop stack //извлечь вершину стека + let y,s2 = pop s //извлечь вершину полученного стека + let result = x + y //вычислить арифметическое выражение + push result s2 //добавить его обратно в стек let MUL stack = - let x,s = pop stack //pop the top of the stack - let y,s2 = pop s //pop the result stack - let result = x * y //do the math - push result s2 //push back on the doubly-popped stack + let x,s = pop stack //извлечь вершину стека + let y,s2 = pop s //извлечь вершину полученного стека + let result = x * y //вычислить арифметическое выражение + push result s2 //добавить его обратно в стек ``` > Test these interactively: -Проверка интерактивном режиме: +Проверка в интерактивном режиме: ```fsharp let add1and2 = EMPTY |> ONE |> TWO |> ADD @@ -354,7 +352,7 @@ let mult2and3 = EMPTY |> TWO |> THREE |> MUL > It works! -Оно работает! +Работает! ### Time to refactor... | Время рефакторинга From 54240c2198cc852630266b50e4cdcf53ade9ae47 Mon Sep 17 00:00:00 2001 From: Anna Date: Thu, 14 Mar 2019 11:49:24 +0300 Subject: [PATCH 2/3] Editet stack-based-calculator.md till the end --- posts/stack-based-calculator.md | 126 ++++++++++++++++---------------- 1 file changed, 61 insertions(+), 65 deletions(-) diff --git a/posts/stack-based-calculator.md b/posts/stack-based-calculator.md index 41498c9..540d7dc 100644 --- a/posts/stack-based-calculator.md +++ b/posts/stack-based-calculator.md @@ -358,27 +358,27 @@ let mult2and3 = EMPTY |> TWO |> THREE |> MUL > It is obvious that there is significant duplicate code between these two functions. How can we refactor? -Очевидно, что присутствует значительное количество повторяющегося кода между этими двумя функциями. Но как провести рефакторинг? +Очевидно, что в этих двух функциях значительное количество кода дублируется. Как мы можем это исправить? > Both functions pop two values from the stack, apply some sort of binary function, and then push the result back on the stack. This leads us to refactor out the common code into a "binary" function that takes a two parameter math function as a parameter: -Обе функции извлекают два значения из стека, применяют к ним какую-нибудь бинарную функцию, после чего помещают результат обратно в стек. Можно вывести общий код в функцию "binary", которая принимает математическую функцию с двумя параметрами: +Обе функции извлекают два значения из стека, применяют к ним некую бинарную функцию, после чего помещают результат обратно в стек. Можно вывести общий код в функцию "binary", которая принимает математическую функцию с двумя параметрами: ```fsharp let binary mathFn stack = - // pop the top of the stack + // вытолкнуть вершину стека let y,stack' = pop stack - // pop the top of the stack again + // снова вытолкнуть вершину стека let x,stack'' = pop stack' - // do the math + // вычислить let z = mathFn x y - // push the result value back on the doubly-popped stack + // положить результат обратно в стек push z stack'' ``` > *Note that in this implementation, I've switched to using ticks to represent changed states of the "same" object, rather than numeric suffixes. Numeric suffixes can easily get quite confusing.* -_В данной реализации разные версии "одного" объекта помечены различным количеством одинарных кавычек. Это сделано потому, что числовые суффиксы как правило могут легко запутать._ +_Обратите внимание, что в этой реализации разные версии "одного" объекта помечены различным количеством штрихов-кавычек. Это сделано потому, что числовые суффиксы могут легко привести к путанице._ > Question: why are the parameters in the order they are, instead of `mathFn` being after `stack`? @@ -386,7 +386,7 @@ _В данной реализации разные версии "одного" > Now that we have `binary`, we can define ADD and friends more simply: -Теперь когда есть `binary`, можно гораздо проще определить ADD и другие функции: +Теперь, когда есть функция `binary`, можно гораздо проще определить ADD и другие функции: > Here's a first attempt at ADD using the new `binary` helper: @@ -398,7 +398,7 @@ let ADD aStack = binary (fun x y -> x + y) aStack > But we can eliminate the lambda, as it is *exactly* the definition of the built-in `+` function! Which gives us: -Можно исключить лямбду, т.к. она представляет *точное* определение встроенной функции `+`: +Но можно избавиться от лямбды, т.к. она представляет *точное* определение встроенной функции `+`: ```fsharp let ADD aStack = binary (+) aStack @@ -406,7 +406,7 @@ let ADD aStack = binary (+) aStack > And again, we can use partial application to hide the stack parameter. Here's the final definition: -Опять же, можно использовать частичное применение, чтобы скрыть параметр стека. Финальное определение: +Опять же, можно использовать частичное применение, чтобы скрыть параметр "стек". Итоговое определение: ```fsharp let ADD = binary (+) @@ -438,8 +438,8 @@ let add1and2thenSub3 = EMPTY |> ONE |> TWO |> ADD |> THREE |> SUB ```fsharp let unary f stack = - let x,stack' = pop stack //pop the top of the stack - push (f x) stack' //push the function value on the stack + let x,stack' = pop stack + push (f x) stack' ``` > And then define some unary functions: @@ -453,29 +453,29 @@ let SQUARE = unary (fun x -> x * x) > Test interactively again: -Интерактив: +Интерактивный режим: ```fsharp let neg3 = EMPTY |> THREE|> NEG let square2 = EMPTY |> TWO |> SQUARE ``` -## Putting it all together | Сбор всего вместе +## Putting it all together | Собираем всё вместе > In the original requirements, we mentioned that we wanted to be able to show the results, so let's define a SHOW function. -В изначальных требования упоминалось, что мы хотели показать результаты, поэтому стоит определить функцию SHOW. +В изначальных требованиях упоминалось, что мы хотели бы иметь возможность показать результаты, поэтому стоит определить функцию SHOW. ```fsharp let SHOW stack = let x,_ = pop stack printfn "The answer is %f" x - stack // keep going with same stack + stack // продолжаем работать с тем же стеком ``` > Note that in this case, we pop the original stack but ignore the diminished version. The final result of the function is the original stack, as if it had never been popped. -Обратите внимание, что в данном случае новая версия стека получаемая через `pop` игнорируется. Конечным же результатом объявляется исходный стек, как будто он никогда не изменялся. +Обратите внимание, что в данном случае новая версия стека, полученная через `pop`, игнорируется. Конечным же результатом объявляется исходный стек, как будто он никогда не изменялся. > So now finally, we can write the code example from the original requirements @@ -485,7 +485,7 @@ let SHOW stack = EMPTY |> ONE |> THREE |> ADD |> TWO |> MUL |> SHOW ``` -### Going further | Идем дальше +### Going further | Идём дальше > This is fun -- what else can we do? @@ -496,20 +496,20 @@ EMPTY |> ONE |> THREE |> ADD |> TWO |> MUL |> SHOW Можно определить несколько дополнительных функций: ```fsharp -/// Duplicate the top value on the stack +/// Дублирование значения на вершине стека let DUP stack = - // get the top of the stack + // получение вершины стека let x,_ = pop stack - // push it onto the stack again + // добавление её обратно в стек push x stack -/// Swap the top two values +/// Обменять местами два верхних значения let SWAP stack = let x,s = pop stack let y,s' = pop s push y (push x s') -/// Make an obvious starting point +/// Создать начальную точку let START = EMPTY ``` @@ -538,7 +538,7 @@ START > But that's not all. In fact, there is another very interesting way to think about these functions. -Но и это еще не все, на самом деле, существует другой интересный способ представления данных функций. +Но и это ещё не всё. На самом деле, существует другой интересный способ представления этих функций. > As I pointed out earlier, they all have an identical signature: @@ -550,62 +550,58 @@ Stack -> Stack > So, because the input and output types are the same, these functions can be composed using the composition operator `>>`, not just chained together with pipes. -Т.к. ввод и вывод имеют одинаковые типы, эти функции могут быть скомпонованы еще и при помощи `>>`, а не только посредством конвейерных операторов. +Поскольку ввод и вывод имеют одинаковые типы, эти функции могут быть скомпонованы еще и при помощи оператора композиции `>>`, а не только посредством конвейерных операторов. > Here are some examples: Несколько примеров: ```fsharp -// define a new function +// определение новой функции let ONE_TWO_ADD = ONE >> TWO >> ADD -// test it START |> ONE_TWO_ADD |> SHOW -// define a new function +// определяем ещё одну let SQUARE = DUP >> MUL -// test it START |> TWO |> SQUARE |> SHOW -// define a new function +// и ещё одна функция let CUBE = DUP >> DUP >> MUL >> MUL -// test it START |> THREE |> CUBE |> SHOW -// define a new function +// и ещё let SUM_NUMBERS_UPTO = DUP // n >> ONE >> ADD // n+1 >> MUL // n(n+1) >> TWO >> SWAP >> DIV // n(n+1) / 2 -// test it START |> THREE |> SQUARE |> SUM_NUMBERS_UPTO |> SHOW ``` > In each of these cases, a new function is defined by composing other functions together to make a new one. This is a good example of the "combinator" approach to building up functionality. -В каждом из этих примеров определяется новая функция составленная из других функций. Это хороший пример "комбинаторного" подхода к построению функциональности. +В каждом из этих примеров новая функция определяется с помощью композиции других функций. Это хороший пример "комбинаторного" подхода к построению функциональности. ## Pipes vs composition | Конвейеры против композиции > We have now seen two different ways that this stack based model can be used; by piping or by composition. So what is the difference? And why would we prefer one way over another? -Были показаны два различных способа использования данной основанной на стеке модели; при помощи конвейеров и композиций. Но в чем разница? И почему надо предпочесть один из способов другому? +Мы видели два различных способа использования нашей модели; при помощи конвейеров и композиции. Но в чем разница? И почему надо предпочесть один из способов другому? > The difference is that piping is, in a sense, a "realtime transformation" operation. When you use piping you are actually doing the operations right now, passing a particular stack around. -Разница в том, что pipe-ы в некотором смысле являются операцией "в реальном времени". В момент использования конвейера операции выполняются прямо сейчас, через передачу определенного стека. +Разница заключается в том, что конвейеры в некотором смысле являются операцией "трансформации в реальном времени". В момент использования конвейера операции выполняются сразу, через передачу определенного стека. > On the other hand, composition is a kind of "plan" for what you want to do, building an overall function from a set of parts, but *not* actually running it yet. -С другой стороны, композиция является разновидностью "плана", который мы хотим осуществить, построение функций из набора составляющих без непосредственного применения. +С другой стороны, композиция - это нечто вроде "плана", который мы хотим осуществить, построение функций из набора составляющих без непосредственного применения. > So for example, I can create a "plan" for how to square a number by combining smaller operations: @@ -656,25 +652,25 @@ let LAMBDA_SQUARE = unary (fun x -> x * x) > Here's the complete code for all the examples so far. -Полный код для всех примеров представленных ранее: +Полный код для всех примеров, представленных выше: ```fsharp // ============================================== -// Types +// Типы // ============================================== type Stack = StackContents of float list // ============================================== -// Stack primitives +// Стековые примитивы // ============================================== -/// Push a value on the stack +/// Поместить значение в стек let push x (StackContents contents) = StackContents (x::contents) -/// Pop a value from the stack and return it -/// and the new stack as a tuple +/// Вытолкнуть значение из стека и вернуть его +/// и новый стек в виде пары let pop (StackContents contents) = match contents with | top::rest -> @@ -684,62 +680,62 @@ let pop (StackContents contents) = failwith "Stack underflow" // ============================================== -// Operator core +// Ядро (операторы) // ============================================== -// pop the top two elements -// do a binary operation on them -// push the result +// вытолкнуть два верхних элемента +// применить к ним бинарную операцию +// положить результат в стек let binary mathFn stack = let y,stack' = pop stack let x,stack'' = pop stack' let z = mathFn x y push z stack'' -// pop the top element -// do a unary operation on it -// push the result +// вытолкнуть вершину стека +// применить к ней унарную операцию +// положить результат в стек let unary f stack = let x,stack' = pop stack push (f x) stack' // ============================================== -// Other core +// Ядро (остальное) // ============================================== -/// Pop and show the top value on the stack +/// Вытолкнуть и напечатать вершину стека let SHOW stack = let x,_ = pop stack printfn "The answer is %f" x - stack // keep going with same stack + stack // продолжаем с тем же стеком -/// Duplicate the top value on the stack +/// Дублировать вершину стека let DUP stack = let x,s = pop stack push x (push x s) -/// Swap the top two values +/// Обменять два верхних значения местами let SWAP stack = let x,s = pop stack let y,s' = pop s push y (push x s') -/// Drop the top value on the stack +/// Удалить вершину стека let DROP stack = - let _,s = pop stack //pop the top of the stack - s //return the rest + let _,s = pop stack //вытолкнуть вершину стека + s //вернуть всё остальное // ============================================== -// Words based on primitives +// Слова, построенные на примитивах // ============================================== -// Constants +// Костанты // ------------------------------- let EMPTY = StackContents [] let START = EMPTY -// Numbers +// Числа // ------------------------------- let ONE = push 1.0 let TWO = push 2.0 @@ -747,7 +743,7 @@ let THREE = push 3.0 let FOUR = push 4.0 let FIVE = push 5.0 -// Math functions +// MАрифметические функции // ------------------------------- let ADD = binary (+) let SUB = binary (-) @@ -758,7 +754,7 @@ let NEG = unary (fun x -> -x) // ============================================== -// Words based on composition +// Слова, построенные с помощью композиции // ============================================== let SQUARE = @@ -778,11 +774,11 @@ let SUM_NUMBERS_UPTO = > So there we have it, a simple stack based calculator. We've seen how we can start with a few primitive operations (`push`, `pop`, `binary`, `unary`) and from them, build up a whole domain specific language that is both easy to implement and easy to use. -У нас получился простой калькулятор на основе стека. Мы увидели как начиная с нескольких примитивных операций (`push`, `pop`, `binary`, `unary`) и других, можно построить полноценную DSL, легко написанную, легко используемую. +У нас получился простой калькулятор на основе стека. Мы увидели, как, начиная с нескольких примитивных операций (`push`, `pop`, `binary`, `unary`) и других, можно построить полноценный DSL, простой в реализации и использовании. > As you might guess, this example is based heavily on the Forth language. I highly recommend the free book ["Thinking Forth"](http://thinking-forth.sourceforge.net/), which is not just about the Forth language, but about (*non* object-oriented!) problem decomposition techniques which are equally applicable to functional programming. -Как можно догадаться, данный пример был в изрядной степени основан на языке Forth. Я очень рекомендую бесплатную книгу ["Thinking Forth"](http://thinking-forth.sourceforge.net/), которая повествует не только об языке Forth, но и об других (_не_ объектно ориентированных!) методах декомпозиции задач, которые одинаково применимы к функциональному программированию в целом. +Как можно догадаться, данный пример был в изрядной степени основан на языке Forth. Я очень рекомендую бесплатную книгу ["Thinking Forth"](http://thinking-forth.sourceforge.net/), которая повествует не только о языке Forth, но и о других (_не_ объектно-ориентированных!) методах декомпозиции задач, которые одинаково применимы к функциональному программированию в целом. > I got the idea for this post from a great blog by [Ashley Feniello](http://blogs.msdn.com/b/ashleyf/archive/2011/04/21/programming-is-pointless.aspx). If you want to go deeper into emulating a stack based language in F#, start there. Have fun! From b75fb1eda5486aaaacb8c7885045b7b387f81354 Mon Sep 17 00:00:00 2001 From: Roman Melnikov Date: Fri, 5 Apr 2019 06:27:34 +0300 Subject: [PATCH 3/3] Update stack-based-calculator.md --- posts/stack-based-calculator.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/posts/stack-based-calculator.md b/posts/stack-based-calculator.md index 540d7dc..176d2e3 100644 --- a/posts/stack-based-calculator.md +++ b/posts/stack-based-calculator.md @@ -44,7 +44,7 @@ type Stack = float list > But, hold on, let's wrap it in a [single case union type](../posts/discriminated-unions.md#single-case) to make it more descriptive, like this: -Но лучше обернуть его в [single case union type](../posts/discriminated-unions.md#single-case), чтобы сделать тип более наглядным, например так: +Но лучше обернуть его в [single case union type](https://fsharpforfunandprofit.com/posts/discriminated-unions/#single-cases), чтобы сделать тип более наглядным, например так: ```fsharp type Stack = StackContents of float list @@ -52,7 +52,7 @@ type Stack = StackContents of float list > For more details on why this is nicer, read the discussion of single case union types in [this post](../posts/discriminated-unions.md#single-case). -Почему лучше делать именно так, можно прочитать [здесь](../posts/discriminated-unions.md#single-case). +Почему лучше делать именно так, можно прочитать [здесь](https://fsharpforfunandprofit.com/posts/discriminated-unions/#single-cases). > Now, to create a new stack, we use `StackContents` as a constructor: @@ -107,7 +107,7 @@ let push x aStack = > Next, what should the order of the parameters be? Should the stack parameter come first or last? If you remember the discussion of [designing functions for partial application](../posts/partial-application), you will remember that the most changeable thing should come last. You'll see shortly that this guideline will be born out. -Во вторых, почему параметры идут именно в таком порядке? Почему стек должен идти первым или последним? В разделе[проектирование функций с частичным применением](../posts/partial-application) говорилось, что наиболее часто меняющийся параметр должен идти последним. Вскоре можно будет убедиться, что эти рекомендации соблюдаются. +Во вторых, почему параметры идут именно в таком порядке? Почему стек должен идти первым или последним? В разделе[проектирование функций с частичным применением](https://habr.com/ru/company/microsoft/blog/430622/) говорилось, что наиболее часто меняющийся параметр должен идти последним. Вскоре можно будет убедиться, что эти рекомендации соблюдаются. > Finally, the function can be made more concise by using pattern matching in the function parameter itself, rather than using a `let` in the body of the function. @@ -138,7 +138,7 @@ val push : float -> Stack -> Stack > In this case, I could probably guess what it did from the signature alone, even without knowing that the name of the function was "push". > This is one of the reasons why it is a good idea to have explicit type names. If the stack type had just been a list of floats, it wouldn't have been as self-documenting. -Как говорилось [ранее](../posts/function-signatures), сигнатура сообщает нам очень многое. +Как говорилось [ранее](https://habr.com/ru/company/microsoft/blog/433402/), сигнатура сообщает нам очень многое. В данном случае я мог бы догадаться, что делает данная функция, лишь по ее сигнатуре, даже не зная, что она называется "push". Это еще одна причина по которой было хорошей идеей иметь явные имена типа. Если бы стек был лишь списком чисел с плавающей точкой, то функция не была бы столь самодокументированной. @@ -285,7 +285,7 @@ let pop (StackContents contents) = > * Option 1: Return a special "Success" or "Error" state, as we did in a [post from the "why use F#?" series](../posts/correctness-exhaustive-pattern-matching.md). > * Option 2: Throw an exception. -* Вариант 1: Вернуть специальное состояние "Success" или "Error", как это делалось в [посте из серии "why use F#?"](../posts/correctness-exhaustive-pattern-matching.md). +* Вариант 1: Вернуть специальное состояние "Success" или "Error", как это делалось в [посте из серии "why use F#?"](https://fsharpforfunandprofit.com/posts/correctness-exhaustive-pattern-matching/). * Вариант 2: Выбросить исключение. > Generally, I prefer to use error cases, but in this case, we'll use an exception. So here's the `pop` code changed to handle the empty case: @@ -743,7 +743,7 @@ let THREE = push 3.0 let FOUR = push 4.0 let FIVE = push 5.0 -// MАрифметические функции +// Арифметические функции // ------------------------------- let ADD = binary (+) let SUB = binary (-)