【问题标题】:Unexpected behavior with function composition函数组合的意外行为
【发布时间】:2016-12-29 23:20:20
【问题描述】:

我正在编写一个小实用函数来将字符串从一种单词分隔方案转换为另一种。整个项目使用lodash,我知道它带有_.camelCase 之类的东西,但我觉得不利用那些方案转换助手更具可扩展性。

这个想法是其他开发人员可以轻松地将他们自己的方案定义添加到我已经拥有的方案定义中:

const CASES = [

    {name: 'lower_kebab', pattern: /^[a-z]+(_[a-z]+)*$/g,
     to_arr: w=> w.split('_'),
     to_str: a=> a.map(w=>w.toLowerCase()).join('_')},

    {name: 'UpperCamel', pattern: /^([A-Z][a-z]*)+$/g,
     to_arr: w=> w.match(/[A-Z][a-z]*/g),
     to_str: a=> a.map(_.capitalize).join('')},

    //...
];

所以每个Case 都需要一个模式来确定一个字符串是否属于该方案,一个to_arr 来适当地分割字符串,一个to_str 来将一个单词数组加入到该方案的字符串中(@ 987654327@ 是可选的,但最好是描述性的)。我已将这两个包括在内,因为它在从 lower_kebabUpperCamel 的转换中出现了一些意外行为。

我已经像这样实现了实际的转换功能:

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/lodash/4.17.3/lodash.min.js"></script>
<script src="https://cdn.jsdelivr.net/lodash/4.17.3/lodash.fp.min.js"></script>

<script>
$(document).ready(()=>{

  var CASES = [
    
    { name: 'lower_kebab', pattern: /^[a-z]+(_[a-z]+)*$/g,
      to_arr: w=> w.split('_'),
      to_str: a=> a.map(w=>w.toLowerCase()).join('_')
    },
     
    { name: 'UpperCamel', pattern: /^([A-Z][a-z]*)+$/g,
      to_arr: w=> w.match(/[A-Z][a-z]*/g),
      to_str: a=> a.map(_.capitalize).join('')
    },
    
    //...
    
  ];
  
  function convert_to(target_scheme_example){
    return _.compose(
            CASES.find(c=>c.pattern.test(target_scheme_example)).to_str
    , str=> CASES.find(c=>c.pattern.test(str)).to_arr(str) );
  }
  
  $('#go').on('click', ()=> $('#result').text(
      convert_to( $('#dst').val() )( $('#src').val() )
  ));
  
});
 
</script>

<p>Try "<strong>UpperCamel</strong>" to "<strong>lower_kebab</strong>" and vice-versa.</p>
<input id="dst" value="UpperCamel" placeholder="Example of target scheme">
<input id="src" value="lower_kebab" placeholder="String to convert">

<button id="go">Convert</button>

<div>
  <p><strong>Result:</strong></p>
  <p id="result"></p>
</div>

“真实”版本严格存在于服务器端代码中,因此 sn-p 中所有与 DOM 相关的内容纯粹用于演示目的(“真实”版本还使用 _.get 进行了一些错误检查为简洁起见,我在这里排除了)。

这就是事情变得奇怪的地方。

在服务器端,问题表现为convert_to('UpCa')('activity_template') 评估为"Activity_template""activity template" 之类的东西。在演示 sn-p 中,我相信同样的问题表现为只能单击“转换”一次而不会引发异常。

有什么想法吗?我的正则表达式有点不对劲吗?我是否误解了如何使用_.compose?如果工具只是坏了,那将是一回事,但它确实让我对它在许多情况下的工作方式感到困惑,但不是全部。

【问题讨论】:

  • CASES.find(c=&gt;c.pattern.test(target_scheme_example)).to_str 不保留任何关于 Javascript 中对象的 to_str 方法的任何信息。不过,您的 to_strs 似乎不需要该信息。
  • 我假设compose(或者更确切地说,compose 的结果)会这样做,即,为了运行f(g(x)),我需要保留fg。有没有什么好的方法可以达到这个效果?
  • 您是否期望_.compose(f, g)g 之前应用f?看起来您对此有所期待。
  • 我可以看到它的样子,但我认为我的构图顺序是正确的。要应用的第一个函数(compose 的最后一个参数)应该是f:String-&gt;Array,这是to_arr 的签名。第一个论点的类似故事,但 to_str
  • 您在 string->array 部分使用c=&gt;c.pattern.test(str),这需要str 已经匹配模式。也许你把strtarget_scheme_example 搞混了。

标签: javascript lodash


【解决方案1】:

来自documentation on RegExp#test

在同一个全局正则表达式实例上多次调用test() 将超过上一次匹配。

这就是它只在第一次工作的原因:正则表达式对象 (pattern) 保持之前对其执行 test 方法所产生的状态。

为避免这种行为,您可以执行以下操作之一:

  1. pattern 正则表达式中删除 g 修饰符,因为它们对于您尝试执行的匹配类型不是必需的,或者

  2. 改用String#match 方法,交换字符串和正则表达式的位置:

    return _.compose(
        CASES.find(c=>target_scheme_example.match(c.pattern)).to_str
                 , str=> CASES.find(c=>str.match(c.pattern)).to_arr(str) );
    }
    

【讨论】:

  • 在实际应用中,我可能通过将CASES 设置为const,使自己陷入了一种虚假的安全感。从每个案例的模式中删除全局标志就可以了!
  • 确实,const 只禁止重新赋值给有问题的变量,而不是它的突变。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-05-01
  • 2017-02-13
  • 1970-01-01
  • 2016-11-08
  • 2016-04-16
  • 1970-01-01
  • 2020-04-10
相关资源
最近更新 更多