很高兴您找到答案并仍然发布问题。我认为这是一个很好的学习方式。
为了进行函数组合练习,这就是我如何构建你的函数。
see 把事情简单化下面是我如何用实际代码处理这个问题
const comp = f => g => x => f(g(x))
const ord = char => char.charCodeAt(0)
const isBetween = (min,max) => x => (x >= min && x <= max)
const isDigit = comp (isBetween(48,57)) (ord)
const not = x => !x
const notDigit = comp (not) (isDigit)
console.log(isDigit('0')) // true
console.log(isDigit('1')) // true
console.log(isDigit('2')) // true
console.log(isDigit('a')) // false
console.log(notDigit('0')) // false
console.log(notDigit('a')) // true
代码审查
顺便说一句,你使用默认参数和泄露的私有 API 所做的事情非常奇怪
// charC is leaked API
const isDigit = (char, charC = char.charCodeAt(0)) => (charC > 47 && charC < 58);
isDigit('9') // true
isDigit('9', 1) // false wtf
isDigit('a', 50) // true wtf
我知道你可能正在这样做,所以你不必写这个
// I'm guessing you want to avoid this
const isDigit = char => {
let charC = char.charCodeAt(0)
return charC > 47 && charC < 58
}
...但该函数实际上要好得多,因为它不会泄漏私有 API(charC
var) 给外部调用者
你会注意到我解决这个问题的方法是使用我自己的isBetween
组合器和柯里化导致了一个非常干净的实现,我认为
const comp = f => g => x => f(g(x))
const ord = char => char.charCodeAt(0)
const isBetween = (min,max) => x => (x >= min && x <= max)
const isDigit = comp (isBetween(48,57)) (ord)
更多的代码会执行这种可怕的默认参数操作
// is suspect you think this is somehow better because it's a one-liner
// abusing default parameters like this is bad, bad, bad
const removeLeadingChars: (str: string) => string =
(str, arr = str.split(''),
dummy = pipe(isDigit, notFun), cnt = recCountWhile(arr, dummy, 0)) =>
arr.slice(cnt).reduce((acc, e) => acc.concat(e),'');
尽量避免为了让一切变得简单而牺牲代码的质量。上面的功能比这里的功能差很多
// huge improvement in readability and reliability
// no leaked variables!
const removeLeadingChars: (str: string) => string = (str) => {
let arr = str.split('')
let dummy = pipe(isDigit, notFun)
let count = recCountWhile(arr, dummy, 0)
return arr.slice(count).reduce((acc, e) => acc.concat(e), '')
}
把事情简单化
您可以...保留它,而不是将字符串拆分为数组,然后迭代数组以计算前导非数字,然后根据计数分割数组的头部,最后将数组重新组装为输出字符串简单的
const isDigit = x => ! Number.isNaN (Number (x))
const removeLeadingNonDigits = str => {
if (str.length === 0)
return ''
else if (isDigit(str[0]))
return str
else
return removeLeadingNonDigits(str.substr(1))
}
console.log(removeLeadingNonDigits('hello123abc')) // '123abc'
console.log(removeLeadingNonDigits('123abc')) // '123abc'
console.log(removeLeadingNonDigits('abc')) // ''
所以,是的,我不确定你问题中的代码是否只是为了练习,但如果这确实是最终目标,那么确实有一种更简单的方法可以从字符串中删除前导非数字。
The removeLeadningNonDigits
这里提供的函数是纯函数,不会泄漏私有变量,处理其给定域(字符串)的所有输入,并保持易于阅读的风格。我会建议这个(或其他东西likethis)与您问题中建议的解决方案进行比较。
函数组合与“管道”
组合两个函数通常按从右到左的顺序完成。有些人发现很难阅读/推理,所以他们想出了一个从左到右的函数作曲家,大多数人似乎都同意这一点pipe
是个好名字。
你的没有任何问题pipe
实现,但我认为很高兴看到如果您努力使事情尽可能简单,生成的代码会变得干净一些。
const identity = x => x
const comp = (f,g) => x => f(g(x))
const compose = (...fs) => fs.reduce(comp, identity)
或者如果你想和我的咖喱一起工作comp
之前在帖子中介绍过
const identity = x => x
const comp = f => g => x => f(g(x))
const uncurry = f => (x,y) => f(x)(y)
const compose = (...fs) => fs.reduce(uncurry(comp), identity)
这些功能中的每一个都有其自己独立的效用。所以如果你定义compose
这样,您就可以免费获得其他 3 个功能。
将此与pipe
您的问题中提供的实施:
const pipe = (...fns) => x => fns.reduce((v, f) => f(v), x);
同样,这很好,但它将所有这些东西混合在一个函数中。
-
(v,f) => f(v)
本身就是有用的函数,为什么不单独定义它,然后通过名称使用它呢?
- the
=> x
正在合并具有无数用途的恒等函数