Adding nCr Function to DDMathParser (with variable

2019-09-15 18:53发布

问题:

I'm struggling to add a new function (nCr) to DDMathParser. I've tried to follow these instructions by the author of DDMathParser:

https://stackoverflow.com/a/15599814/2521277

My problem: I can't manage to combine DDExpressions to a complex expression with variables...

Can you help me to add the function nCr (http://en.wikipedia.org/wiki/Binomial_coefficient#Factorial_formula)

Thank you!

回答1:

DDMathParser author here.

The basic signature for all functions is this:

DDExpression* ^(NSArray *arguments, NSDictionary *variables, DDMathEvaluator *evaluator, NSError **error);

So you need to create one of these blocks that implements the nCr algorithm. There are a couple of ways you could do it:

  1. You could implement the function exactly as shown in the article, calculating the factorials, doing the subtraction, and doing the division.
  2. You could implement the function wholly in terms of the existing factorial, subtraction, and division functions.
  3. You could do a combination of the two.

Personally, I'd recommend the last one, because it saves having to re-evaluate the arguments, but also means you don't have to write most of the logic yourself.

It'd be something like this:

DDMathFunction nCrFunction = ^(NSArray *args, NSDictionary *vars, DDMathEvaluator *eval, NSError **error) {
  if ([args count] != 2) {
    *error = [NSError errorWithDomain:DDMathParserErrorDomain code:DDErrorCodeInvalidNumberOfArguments userInfo:@{NSLocalizedDescriptionKey : @"nCr requires 2 arguments"}];
    return nil;
  }

  DDExpression *first = [args objectAtIndex:0];
  DDExpression *second = [args objectAtIndex:1];

  NSNumber *n = [first evaluateWithSubstitutions:vars evaluator:eval error:error];
  if (n == nil) { return nil; }

  NSNumber *k = [second evaluateWithSubstitutions:vars evaluator:eval error:error];
  if (k == nil) { return nil; }

  // some validation here to guarantee that 0 ≤ k ≤ n

  // now, re-box the numbers in expressions to pass off to other functions
  DDExpression *nExpression = [DDExpression numberExpressionWithNumber:n];
  DDExpression *kExpression = [DDExpression numberExpressionWithNumber:k];

  // build the algorithm
  DDExpression *f1 = [DDExpression functionExpressionWithFunction:DDOperatorFactorial arguments:@[kExpression] error:error]; // k!

  // the other half of the denominator
  DDExpression *subtract = [DDExpression functionExpressionWithFunction:DDOperatorMinus arguments:@[nExpression, kExpression] error:error]; // (n-k)
  DDExpression *f2 = [DDExpression functionExpressionWithFunction:DDOperatorFactorial arguments:@[subtract] error:error]; // (n-k)!

  // the full denominator
  DDExpression *denominator = [DDExpression functionExpressionWithFunction:DDOperatorMultiply arguments:@[f1, f2] error:error]; // k!(n-k)!

  // the numerator
  DDExpression *numerator = [DDExpression functionExpressionWithFunction:DDOperatorFactorial arguments:@[nExpression] error:error]; // n!

  // the whole thing
  DDExpression *final = [DDExpression functionExpressionWithFunction:DDOperatorDivide arguments:@[numerator, denominator] error:error]; // n!/(k!(n-k)!)

  return final;
};

With this block, you can register the function on your DDMathEvaluator:

[[DDMathEvaluator sharedMathEvaluator] registerFunction:nCrFunction forName:@"nCr"];

That's it!

Now you can do:

NSNumber *n = [@"nCr(4, 3)" numberByEvaluatingString];

Warning: code typed in a browser and not compiled. Caveat Implementor


Incidentally, if this is a function you'd like to see built-in to DDMathParser, please open a new issue over at Github.