函数调用过程中所有参数的提取

1. 引言

曾经遇到一个面试题:如何将函数调用看作一个字符串,提取出所有的参数并构建成一个树。

比如输入字符串 func1(123, 456), 输出是

func1(123, 456) 
    |
    ---------123
    |
    ---------456

输入字符串 func1(123, 456, func2(78, 99)) 输出则是

func1(123, 456, func2(78, 99))
        |
        -----------------------------123
        |
        ----------------------------456
        |
        ----------------------------func2(78, 99)
                      |
                      --------------78
                      |
                      --------------99

面试过程中,假设了函数调用肯定是合法的,即函数左右括号肯定匹配,函数参数都是double 或者 int 等数值类型。

由于存在这样的假设前提,题目已经变得相对简单多,但是实际项目中函数的参数是多种多样的,参数可以是String 类型,并且实参包含逗号或者左右括号都是可能的。

非常巧的是最近在项目中就遇到了类似的算法,需要将函数调用过程中所有满足一定格式的参数提取出来,当然参数有字符串并以单引号为开头结尾。但是单引号不再包含单引号。

今天就给出一个针对函数所有参数提取的C#实现。

2. 代码实现

首先给出了一个简单的Tree类的定义,熟悉C++的同学肯定不陌生,这里用C#实现:

         public class TreeNode
        {
            public string Text { get; set; }
            public List<TreeNode> ChildNodes { get; set; }
        }

定义函数:判断参数是不是嵌套的函数调用,例如函数参数只可能是以下类型,

数值类型: 123 或者 56.87

字符类型: ‘fake parameter’ 或者 ‘fake paramter with left bracket ( or right bracket ).’

嵌套函数: funcx(34,78, ‘dd’) 或者 funcy()

        public static bool IsFunctionCall(string str)
        {
            if (string.IsNullOrEmpty(str))
            {
                return false;
            }

            if (str.Trim().First( ) == '\'' && str.Trim().Last() == '\'')
            {
                return false;
            }

            int leftBracketIndex = str.IndexOf("(", StringComparison.OrdinalIgnoreCase);
            int rightBracketIndex = str.LastIndexOf(")", StringComparison.OrdinalIgnoreCase);

            if (leftBracketIndex < 0 && rightBracketIndex < 0)
            {
                return false;
            }

            return true;
        }

定义函数:获取函数所有未分割的参数,例如

“func1(123, 456)”=> “123, 456”

“func1(123, 456, func2(78, 99))”=> “123, 456, func2(78, 99)”

        public static string GetParameterString(string function)
        {
            int leftBracketIndex = function.IndexOf("(", StringComparison.OrdinalIgnoreCase);
            int rightBracketIndex = function.LastIndexOf(")", StringComparison.OrdinalIgnoreCase);

            string value = function.Substring(leftBracketIndex + 1, rightBracketIndex - leftBracketIndex - 1).Trim();

            return (string.IsNullOrEmpty(value) || string.IsNullOrWhiteSpace(value)) ? null : value;
        }

调用函数:

        public static TreeNode Parse(string input)
        {
            if (string.IsNullOrEmpty(input))
                return null;

            if (IsFunctionCall(input))
            {
                return ParseFunction(input);
            }
            else
            {
                return new TreeNode() {Text = input};
            }
        }

最后是提取Function 所有参数的函数实现:

        /// <summary>
        /// 通过递归提取所有参数
         /// </summary>
        /// <param name="input">输入参数肯定是一个函数调用比如: func1(123, 456, func2(78, 'abc'))</param>
        /// <returns>返回一个树</returns>
        public static TreeNode ParseFunction(string input)
        {
            if (string.IsNullOrEmpty(input))
                return null;

            //构建顶层树节点并且得到所有的未分割的参数
              TreeNode rootNode = new TreeNode() {Text = input};
            var paramStr = GetParameterString(input);
            if (paramStr == null)
            {
                return rootNode;
            }

            //存放所有参数的List
            List<string> result = new List<string>();
            string tmp = "";

            //可以用Stack 来实现括号的匹配,这里简单的记录左括号的个数
              int bracketCount = 0;

            //参数是否是字符串
              bool hasSingleQuote = false;

            foreach (char c in paramStr)
            {
                if (c == '\'')
                {
                    tmp += c;

                    //字符串结尾
                       if (hasSingleQuote)
                    {
                        hasSingleQuote = false;
                        continue;
                    }

                    //字符串开始
                       hasSingleQuote = true;
                    continue;
                }

                //只有参数不是字符串的时候 左括号才加一
                  if (c == '(' && !hasSingleQuote)
                {
                    bracketCount++;
                    tmp += c;
                }
                //只有参数不是字符串的时候 右括号才减一
                  else if (c == ')' && !hasSingleQuote)
                {
                    bracketCount--;
                    tmp += c;
                }
                //参数参数满足的条件
                  else if (c == ',' && bracketCount == 0 && !hasSingleQuote)
                {
                    result.Add(tmp.Trim());
                    tmp = "";
                }
                else
                {
                    tmp += c;
                }
            }

            //最后一个参数
              if (tmp != "")
            {
                result.Add(tmp.Trim());
            }

            //递归的提取
              rootNode.ChildNodes = result.Select(parm => Parse(parm)).ToList();
            return rootNode;
        }

测试用例:

        static void Main(string[] args)
        {
            TreeNode testTree = Parse("func1(123, 456, func2(78, 'abc'))");
            testTree = Parse(" func1(func4(), 123, '(abc', func3(')dj'), 456, func2(78, 'dev', func5('xx)(x', 99)))");
        }

3. 结论

这道题目其实考察的是对递归和Stack的理解。首先如果直接用逗号分隔只会把问题弄得复杂,只要想到左右括号匹配等条件再利用Stack的思想,问题也就变得简单多了。

如果给出的算法有错误或者有更好的算法 请各位支出。

欢迎访问我的个人网站 51zhang.net 网站还在不断开发中…

原文地址:https://www.cnblogs.com/VectorZhang/p/5447524.html