dimension() : Dimension
to get the dimension vector of our expression. We will use it internally to validate the physical meaning (flies can only be folded with flies). It will be useful to give it “out” as part of the public interface, since it is quite logical that someone, sooner or later, will want to know the dimension of some expression.evaluate() : float
Calculate the absolute value of the expression, i.e. count math.decompose() : MathematicalExpression
decompose all complex units of measure in terms of their decomposition. This method will be useful to us both for calculating the absolute value and for calculating the dimension vector. But again, it sounds quite logical to include this operation in our public interface because someone may need such an operation outside the internal implementation of our calculator.formatQuantity(float $quantity)
enter constants in our expression (and select these constants) so that the expression starts to equal $quantity
. Such a method will be slightly more flexible than simply converting from one unit to another. Here is an example: Americans use feet and inches for human growth. I am 180 cm tall. I could convert it to feet (5.905512) or inches (70.8661457). But for the American brain, the canonical form is to single out full feet and write the remainder as inches - 5 inches 10.8 inches. Then outside you can imagine the following pseudo-code for formatting / converting (maybe 2-3 lines more code, but the design flexibility is an order of magnitude higher): $quantity = $source_expression->evaluate(); $european_height = new MathematicalExpression(“1 * meter”); $european_height->formatQuantity($quantity); print $european_height->toString() . “\n”; // 1.8 * meter $us_height = new MathemticalExpression(“1 * foot + 1 * inch”); $us_height->formatQuantity($quantity); print $us_height->toString() . “\n”; // 5 * foot + 10.8 * inch
addDimension(Dimension $dimension1, Dimension $dimension2) : Dimension
Add one dimension to another. Tritely perform vector addition / subtraction. This will be useful in multiplication operations, when dimensions are added.subtractDimension(Dimension $dimension1, Dimension $dimension2) : Dimension
Subtract one dimension from another. Similarly, with the previous operation, only the sign is inverted. It will be useful when the division operation is processed.isEqual(Dimension $dimension1, Dimension $dimension2) : bool
Compare one dimension with another. The whole validation logic of physical dimensions will be based on this method. With this function, we will understand whether it makes physical sense to add / subtract two expressions.MathematicalExpression
interface.operand1() : MathematicalExpression
get the first (left) operandoperand2() : MathematicalExpression
get the second (right) operandisEqual(Dimension $dimension1, Dimension $dimension2) : bool
function isEqual(Dimension $dimension1, Dimension $dimension2) : bool
can be implemented as follows:formatQuantity()
method. A method called containsDimensionlessMember()
will return a bool and indicate whether this expression can somehow mutate to be numerically equal to some value. Since only a constant affects the numerical value, so the method is called “haveLiTsF“ dimensionlessChlen ”. /** * Determine physical dimension of this mathematical expression. * * @return array * Dimension array of this mathematical expression */ public function dimension() { // . return array(); }
/** * Test whether this mathematical expression includes a dimensionless member. * * Whether this mathematical expression contains at least 1 dimensionless * member. * * @return bool * Whether this mathematical expression contains at least 1 dimensionless * member */ public function containsDimensionlessMember() { return TRUE; }
/** * Format a certain amount of quantity within this mathematical expression. * * @param float $quantity * Quantity to be formatted * * @return MathematicalExpression * Formatted quantity into this mathematical expression. Sometimes the * mathematical expression itself must mutate in order to format the * quantity. So the returned mathematical expression may not necessarily be * the mathematical expression on which this method was invoked. For * example, the expression "unit" would mutate into "1 * unit" in order to * have a dimensionless member and therefore be able to format the $quantity */ public function formatQuantity($quantity) { // “ -”, // . $this->constant = $quantity; return $this; }
/** * Numerically evaluate this mathematical expression. * * @return float * Numerical value of this mathematical expression */ public function evaluate() { // . return $this->constant; }
/** * Decompose (simplify) this mathematical expression. * * @return MathematicalExpression * Decomposed (simplified) version of this mathematical expression */ public function decompose() { // , . return $this; }
/** * Determine physical dimension of this mathematical expression. * * @return array * Dimension array of this mathematical expression */ public function dimension() { // , . // , // . return is_object($this->decomposition) ? $this->decompose()->dimension() : array($this->identifier() => 1); }
/** * Test whether this mathematical expression includes a dimensionless member. * * Whether this mathematical expression contains at least 1 dimensionless * member. * * @return bool * Whether this mathematical expression contains at least 1 dimensionless * member */ public function containsDimensionlessMember() { // , , , // . return FALSE; }
/** * Format a certain amount of quantity within this mathematical expression. * * @param float $quantity * Quantity to be formatted * * @return MathematicalExpression * Formatted quantity into this mathematical expression. Sometimes the * mathematical expression itself must mutate in order to format the * quantity. So the returned mathematical expression may not necessarily be * the mathematical expression on which this method was invoked. For * example, the expression "unit" would mutate into "1 * unit" in order to * have a dimensionless member and therefore be able to format the $quantity */ public function formatQuantity($quantity) { // -, . // – , – . // , . // , $quantity. // , // “ ”. // We expand this unit into "1 * $this" so we get a dimensionless // member that can be formatted. return (new MathematicalExpression(1 * “ . $this->toString()))->formatQuantity($quantity); }
/** * Numerically evaluate this mathematical expression. * * @return float * Numerical value of this mathematical expression */ public function evaluate() { // , // . // ? - , “”, // “ ”. return is_object($this->decomposition) ? $this->decompose()->evaluate() : NULL; }
/** * Decompose (simplify) this mathematical expression. * * @return MathematicalExpression * Decomposed (simplified) version of this mathematical expression */ public function decompose() { // , . if (is_object($this->decomposition)) { return $this->decomposition->decompose(); } // . return $this; }
$this->operator
property, the following parameters are packed in this property:$this->operator['evaluate callback']
.$this->operator['dimension check']
.$this->operator['dimension callback']
.formatQuantity()
method). It is abstracted into $this->operator['split quantity callback']
, which must distribute the absolute value between the first and second operand so that at least one of them has a “beautiful” constant. /** * Determine physical dimension of this mathematical expression. * * @return array * Dimension array of this mathematical expression */ public function dimension() { // . // /, . // /, . , // . // // . // // . “meter ^ 2” “meter ^ 3”. // dimension callback – // , . $dimension_callback = $this->operator['dimension callback']; list($evaluate1, $evaluate2) = $this->evaluateOperands(); return $dimension_callback($this->operand1->dimension(), $this->operand2->dimension(), $evaluate1, $evaluate2); }
/** * Test whether this mathematical expression includes a dimensionless member. * * Whether this mathematical expression contains at least 1 dimensionless * member. * * @return bool * Whether this mathematical expression contains at least 1 dimensionless * member */ public function containsDimensionlessMember() { // , // . return $this->operand1->containsDimensionlessMember() || $this->operand2->containsDimensionlessMember(); }
/** * Format a certain amount of quantity within this mathematical expression. * * @param float $quantity * Quantity to be formatted * * @return MathematicalExpression * Formatted quantity into this mathematical expression. Sometimes the * mathematical expression itself must mutate in order to format the * quantity. So the returned mathematical expression may not necessarily be * the mathematical expression on which this method was invoked. For * example, the expression "unit" would mutate into "1 * unit" in order to * have a dimensionless member and therefore be able to format the $quantity */ public function formatQuantity($quantity) { $contains_dimensionless1 = $this->operand1->containsDimensionlessMember(); $contains_dimensionless2 = $this->operand2->containsDimensionlessMember(); list($quantity1, $quantity2) = $this->evaluateOperands(); // , 2 , // – , // . : (“1 * meter”)->formatQuantity(100); // , .. 2 () // “” . if ($contains_dimensionless1 xor $contains_dimensionless2) { if ($contains_dimensionless1) { $this->operand1->formatQuantity($quantity / $quantity2); } else { $this->operand2->formatQuantity($quantity / $quantity1); } } else { // , . // “1 * foot + 1 * inch”. // , - // “” , . // “” // . // split quantity . $split_quantity = $this->operator['split quantity callback']; list($quantity1, $quantity2) = $split_quantity($quantity, $quantity1, $quantity2, $this->operator); // “” , . $this->operand1->formatQuantity($quantity1); $this->operand2->formatQuantity($quantity2); } return $this; }
/** * Numerically evaluate this mathematical expression. * * @return float * Numerical value of this mathematical expression * * @throws UnitsMathematicalExpressionDimensionException * Exception is thrown if this mathematical expression has inconsistency in * physical dimensions */ public function evaluate() { // , . if ($this->operator['dimension check'] && !units_dimension_equal($this->operand1->dimension(), $this->operand2->dimension())) { throw new UnitsMathematicalExpressionDimensionException(); } list($evaluate1, $evaluate2) = $this->evaluateOperands(); // , // – $evaluate1 $evaluate2 / // , / , // $this->operator. $evaluate_callback = $this->operator['evaluate callback']; return $evaluate_callback($evaluate1, $evaluate2); }
/** * Decompose (simplify) this mathematical expression. * * @return MathematicalExpression * Decomposed (simplified) version of this mathematical expression */ public function decompose() { // // . return new OperatorMathematicalExpression($this->operator, $this->operand1()->decompose(), $this->operand2()->decompose()); }
/** * Retrieve operand #1 from this mathematical operator. * * @return MathematicalExpression * Operand #1 from this mathematical expression */ public function operand1() { return $this->operand1; } /** * Retrieve operand #2 from this mathematical operator. * * @return MathematicalExpression * Operand #2 from this mathematical expression */ public function operand2() { return $this->operand2; }
/** * Numerically evaluate both operands and return them as an array. * * @return array * Array of length 2: the 2 operands numerically evaluated */ protected function evaluateOperands() { $evaluate1 = $this->operand1->evaluate(); $evaluate2 = $this->operand2->evaluate(); // , – , // // , . // , 0. – . // = * 1 6 . // , ( // ) // , . if (is_null($evaluate1)) { $evaluate1 = $this->operator['transparent operand1']; } if (is_null($evaluate2)) { $evaluate2 = $this->operator['transparent operand2']; } return array($evaluate1, $evaluate2); }
function units_operator_add_evaluate($operand1, $operand2) { // . return $operand1 + $operand2; }
function units_operator_add_dimension($dimension1, $dimension2, $operator1, $operator2) { // .. , // . return $dimension1; }
function units_operator_add_split_quantity($total_quantity, $quantity1, $quantity2, $operator) { // : , // ( + ), . // , . .. // “ ” // . $greatest_quantity = max($quantity1, $quantity2); $for_greater_quantity = floor($total_quantity / $greatest_quantity) * $greatest_quantity; $for_fewer_quantity = $total_quantity - $for_greater_quantity; return $quantity1 > $quantity2 ? array($for_greater_quantity, $for_fewer_quantity) : array($for_fewer_quantity, $for_greater_quantity); }
function units_operator_multiply_evaluate($operand1, $operand2) { return $operand1 * $operand2; }
function units_operator_multiply_dimension($dimension1, $dimension2, $operator1, $operator2) { // . return units_dimension_add($dimension1, $dimension2); }
function units_operator_multiply_split_quantity($total_quantity, $quantity1, $quantity2, $operator) { // formatQuantity() // , : // 1 * foot + 1 * inch. 1 * foot 1 * inch , . // – // , // . return array($total_quantity, $operator['transparent operand2']); }
toInfix()
. But we also need the inverse operation — something that can parse an infix expression into our binary tree. This problem is solved rather trivially in the presence of Wikipedia and sensible brain. Read about parsing infix and postfix notation. The presence of such an infix parser makes life much easier for a programmer, since expressions can be assembled by a simple call units_mathematical_expression_create_from_infix(“10 * meter * kilogram / second”)
. And not a cumbersome construction in several lines, where for each operand we create our own object and put one object into another to build the final binary tree.On the Fahrenheit scale, the melting temperature of ice is +32 ° F, and the boiling point of water is +212 ° F (at normal atmospheric pressure). In this case, one degree Fahrenheit is equal to 1/180 of the difference between these temperatures.Perhaps this definition is not so obvious, but Fahrenheit is not converted linearly to Celsius. There is no such factor, multiplying by which, we convert one into another.
Source: https://habr.com/ru/post/359198/
All Articles