"Scala Programming" format arithmetic expression program

Source: Internet
Author: User
Tags class operator

Formatting an arithmetic expression program

To practice the use of pattern matching, this blog post introduces programs that write formatting arithmetic expressions, and the final rendering results are shown in the mathematical expression of the two-dimensional layout below, where the division operation is printed vertically:

1          - * (x + 1)2          -----------  x   1.5    - + ---    2    x   

In order to implement this program, we need to do a bit of work:

1. 编写一个二维布局库来创建和渲染二维布局元素。这里主要应用Scala面向对象编程的一些方法,通过组合与继承来构建简单部件,进而实现库的设计。2. 编写一个表达式的格式化类,利用二维布局库来渲染二维字符图形,从而实现一个完整的呈现效果。
Two-dimensional layout library

In a two-dimensional layout library, we define classes so that element objects can be constructed from simple parts, including arrays, rows, and rectangles. We will also define the combined operators above and beside . This combination operator can synthesize elements of certain areas into new elements.

Implement above and beside

The above method in the element class places one element on top of another, and is actually the value of the contents that connects the two elements. ++connect two arrays using an operator. We also want to consider the width of the two elements here.
The beside method, which puts two elements together, creates a new element, and each row of the new element comes in tandem with the two primitive elements. Here also consider the different heights of the two elements.

Implement widen and heighten

The widen method is called by above to ensure that the element is stacked together with the same width, and the heighten method is beside called to ensure that the elements that are joined together have the same height.

Defining Factory objects

The Factory object contains methods for building other objects. Customers can construct objects by using the factory method instead of directly using new. The advantage of this approach is that you can centralize the creation of objects and hide the details of the classes that the objects actually represent.
Here we create the associated object of the element class and use it as the factory method for the layout element. In this way, the only thing you expose to the client is the combination of the class/object of element, which hides the implementations of the Arrayelement,lineelement and uniformelement three classes.
Three Elem factory methods to create different classes, where parameters come and go to differentiate different classes.

Complete program
 Packagecom.jason.exprImportElement.elemAbstract  class Element {  defContents:array[string]defWidth:int = Contents (0). lengthdefHeight:int = Contents.lengthdefAbove (that:element): Element = {ValThis1 = ThisWiden That.widthValTHAT1 = that widen This. width elem (this1.contents + that1.contents)}defBeside (that:element): Element = {ValThis1 = ThisHeighten That.heightValTHAT1 = that heighten This. Height Elem ( for((line1, line2) <-this1.contents zip that1.contents)yieldLINE1+LINE2)}defWiden (w:int): Element = {if(w <= width) This    Else{Valleft = Elem ("', (W-width)/2, height)Valright = Elem ("', w-width-left.width, height) left beside ThisBeside right}}defHeighten (h:int): Element = {if(H <= height) This    Else{Valtop = Elem ("', width, (h-height)/2)Valbot = Elem ("', width, h-height-top.height) top above ThisAbove bot}}Override deftoString = Contents mkstring"\ n"} Object Element {  Private  class arrayelement(Val contents:array[string]) extends Element   Private  class lineelement(s:string) extends Element {    ValContents = Array (s)Override defwidth = s.lengthOverride defHeight =1}Private  class uniformelement(Ch:char, Overri De Val width:int, override Val height:int) extends  Element {    Private ValLine = ch.tostring * WidthdefContents = Array.fill (height) {line}}defElem (contents:array[string]): Element =NewArrayelement (contents)defElem (Chr:char, Width:int, height:int): Element =NewUniformelement (CHR, width, height)defElem (line:string): Element =NewLineelement (line)}
Formatting expression class operator precedence

First, on a horizontal layout, a structured expression, such as:

BinOp("+",      BinOp("+",            BinOp("+", Var("x"), Var("y")),            Var("z")),      Number(1))

should be printed as (x+y)*z+1 . Note that the parentheses on both sides of the x+y are mandatory, but (x+y)*z both sides are optional. To ensure the best readability of the layout, the goal is to remove the redundant parentheses and keep all the parentheses that must exist.

in operator precedence, you can take an operator group that defines only the increment priority, and then calculate the priority of each operator by calculating the precedence level. This saves a lot of pre-calculations on the priority level.
precedenceVariables are mappings from operators to their precedence, integers starting at 0. It is calculated from a for expression with two generators. The first generator produces opGroups each index of the array i . The second generator produces each of the opGroups(i) operators op . The for expression creates an association from the op operator to its index for each of these operators i . Therefore, the relative position of the operator in the array is taken as its priority.
The handling of operator precedence is this:

ValOpgroups =array (Set ("|","||"), Set ("&","&&"), Set ("^"), Set ("==","!="), Set ("<","<=",">",">="), Set ("+","-"), Set ("*","%"))ValPrecedence = {ValAssocs = for{i <-0Until Opgroups.length op <-opgroups (i)}yieldOp-I Map () + + Assocs}

Now you also need to consider the precedence of the two operators, which are the precedence of the unary and division operators, respectively. The precedence of unary operators is higher than any binary operator, whose priority is 1 greater than the priority of * and%, set unaryPrecedence to the length of opgroups, and the priority of the division operator fractionPrecedence is-1, in which case additional processing and manipulation is performed.

Pattern matching in a format operation

The Main method format is used to produce a layout element that expresses a two-dimensional array of characters that accept an expression and the operator precedence of an expression. The Format method accomplishes its work by performing pattern matching on the type of the expression.
Let's focus on the sample of the unary operator and the two-dollar operator.

case UnOp(op, arg) =>  elem(op) beside format(arg, unaryPrecedence)

Explanation: Unary operations, the result is composed of the result of manipulating OP and the highest environment precedence format parameter arg.

case  BinOp ( "/" , left, right ) = = {Val top = format  (left, fractionprecedence) val bot = format  (right , Fractionprecedence) Val line  = Elem ( '-' , top.width max  bot.width, 1 ) val frac = top above line  above bot if  (Enclprec! = fractionprecedence) frac else  elem () beside Frac beside Elem ()}  

Explanation: If the expression is a fraction, the intermediate result frac is placed vertically by the formatted operand left and right, separated by a horizontal line element. The width of the horizontal line is the maximum value of the formatted operand width.
Finally, add a space on each side of the frac to consider "(A/b)/C" If the result is:

a_b_c

This can be either "(A/b)/C" or "A/(B/C)". To eliminate ambiguous semantics, the embedded fractional A/b layout should have one space on each side. Became like this:

  a  _  b_ _ _   c
case  BinOp (OP, Left, right ) = {val Opprec = Precedence (OP) val l =  Format  (left, Opprec) val r = format  (right , Opprec + 1 ) val oper = l beside Elem ( + OP + ) Beside R if  (Enclprec <= opprec) oper else  elem ( "(" ) beside Oper beside Elem ()}  

Explanation: For the normal two-dollar operator, it is first formatted with its operand left and right, and the priority of the formatted Zuo action element is the Opprec of the operator op, while the formatted rvalue is the priority plus 1. This design ensures that the brackets also reflect the correct union.
Like what:
BinOp("-", Var("a"), BinOp("-", Var("b"), Var("c")))
will be correctly divided into "a-(B-C)", and then the intermediate result oper by the formatted left and right operation of the element in turn, the middle plus operator composition. If the priority of the current operator is less than the precedence of the perimeter operator, then the Oper is placed in parentheses, otherwise it is returned as-is.

Complete program
 Packagecom.jason.exprImportElement.elemSealed  Abstract  class Expr Case   class Var(name:string) extends Expr Case   class number(num:double) extends Expr Case   class unop(operator:string, arg:expr) extends Expr Case   class binop(operator:string, left:expr, right:expr)  extends Expr  class exprformatter {  Private ValOpgroups = Array (Set ("|","||"), Set ("&","&&"), Set ("^"), Set ("==","!="), Set ("<","<=",">",">="), Set ("+","-"), Set ("*","%")    )Private ValPrecedence = {ValAssocs = for{i <-0Until Opgroups.length op <-opgroups (i)}yieldOp-I Map () + + Assocs}Private ValUnaryprecedence = Opgroups.lengthPrivate ValFractionprecedence =-1  Private defFormat (e:expr, enclprec:int): Element = EMatch{ CaseVar (name) = Elem (name) CaseNumber (num) = {defStripdot (s:string) = {if(S EndsWith". 0") S.substring (0, S.length-2)Elses} elem (Stripdot (num.tostring))} CaseUnop (OP, arg) = Elem (OP) beside format (ARG, unaryprecedence) CaseBinop ("/", left, right) = {Valtop = Format (left, fractionprecedence)Valbot = Format (right, fractionprecedence)Valline = Elem ('-', Top.width Max Bot.width,1)ValFrac = top above line above botif(Enclprec! = fractionprecedence) fracElseElem" ") beside Frac beside Elem (" ")    } CaseBinop (OP, left, right) = = {ValOpprec = Precedence (OP)ValL = Format (left, Opprec)ValR = Format (right, Opprec +1)Valoper = l beside Elem (" "+ OP +" ") beside Rif(Enclprec <= Opprec) operElseElem"(") beside Oper beside Elem (")")    }  }defFormat (e:expr): Element = Format (E,0)}
Test results
 Packagecom.jason.expr Object Expression extendsApp{    Valf =NewExprformatterVale1 = Binop ("*", Binop ("/", Number (1), Number (2)), Binop ("+", Var ("x"), Number (1)))ValE2 = Binop ("+", Binop ("/", Var ("x"), Number (2)), Binop ("/", Number (1.5), Var ("x")))ValE3 = Binop ("/", E1, E2)defShow (e:expr) = println (F.format (E) +"\ n") for(e <-Array (E1, E2, E3)) Show (E)}

Final Print Effect:

1          - * (x + 1)2          x   1.5- + ---2    x 1          - * (x + 1)2          -----------  x   1.5    - + ---    2    x   

reprint Please indicate the author Jason Ding and its provenance
Gitcafe Blog Home page (http://jasonding1354.gitcafe.io/)
GitHub Blog Home page (http://jasonding1354.github.io/)
CSDN Blog (http://blog.csdn.net/jasonding1354)
Jane Book homepage (http://www.jianshu.com/users/2bd9b48f6ea8/latest_articles)
Google search jasonding1354 go to my blog homepage

"Scala Programming" format arithmetic expression program

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.