zoukankan      html  css  js  c++  java
  • 函数调用过程中所有参数的提取

    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 网站还在不断开发中…

  • 相关阅读:
    jquery判断浏览器类型
    webservice soapheader验证方法
    webservice跨域文件,好多年前的东西,远程调用,js服务器端使用,可以远程调用
    c# 动态调用webservice 转录一下
    linq使用 count与sum等
    js date扩展方法
    c#深拷贝的一个方法
    javascript jquery document.ready window.onload
    带你上手一款下载超 10 万次的 IDEA 插件
    Knative Serving 健康检查机制分析
  • 原文地址:https://www.cnblogs.com/VectorZhang/p/5447524.html
Copyright © 2011-2022 走看看