這一篇的解答會盡量用 Ramda.js 因為會變很簡潔,若還不熟可以看我上一篇[補充] Ramda.js。
把以下改成 Composition style
// 'Welcome to FP world'
const doStuff = str => {
const lower = str.toLowerCase() // 'welcome to fp world'
const words = lower.split(' ') // ['welcome', 'to', 'fp', 'world']
words.reverse() // ['world', 'fp', 'to', 'welcome']
for(let i in words) {
words[i] = words[i].trim()
} // ['world', 'fp', 'to', 'welcome']
let keepers = []
for(let i in words) {
if(words[i].length > 3) {
keepers.push(words[i])
} // ['world', 'welcome']
}
return keepers.join(' ') // 'world welcome'
}
doStuff('Welcome to FP world')
若有印象的話,這也是 Buzz Word 1 : Declarative vs. Imperative 曾經出現過的例子
// Answer here
const _ = R;
const join = dec => str => str.join(dec);
const trim = str => str.trim();
const doStuff = _.compose(
join(' '),
_.filter(x => x.length > 3),
_.reverse,
_.map(trim),
_.split(' '),
_.toLower
)
let result = doStuff('Welcome to FP world'); // 'world welcome'
這裡有一系列改寫成 Composition style 的小練習
// Example Data
const CARS = [
{name: "Ferrari FF", horsepower: 660, dollar_value: 700000, in_stock: true},
{name: "Spyker C12 Zagato", horsepower: 650, dollar_value: 648000, in_stock: false},
{name: "Jaguar XKR-S", horsepower: 550, dollar_value: 132000, in_stock: false},
{name: "Audi R8", horsepower: 525, dollar_value: 114200, in_stock: false},
{name: "Aston Martin One-77", horsepower: 750, dollar_value: 1850000, in_stock: true},
{name: "Pagani Huayra", horsepower: 700, dollar_value: 1300000, in_stock: false}
];
用 _.compose()
改寫以下函式。 提示: _.prop()
也是 Curry 喔
const isLastInStock_ = cars => {
var reversed_cars = _.last(cars)
return _.prop('in_stock', reversed_cars)
}
isLastInStock(CARS); // false
// Answer here
const isLastInStock = _.compose(
_.prop('in_stock'),
_.last
)
用 _.compose()
、 _.prop()
、 _.head()
來找第一部車子的名稱
// Answer here
const nameOfFirstCar = _.pipe(
_.head,
_.prop('name')
)
nameOfFirstCar(CARS); // "Ferrari FF"
用 _.pipe
改寫以下函式。 提示: 使用 _average
這個 helper function
const _average = function(xs) { return _.reduce(_.add, 0, xs) / xs.length; }; // <- leave be
const averageDollarValue = function(cars) {
const dollar_values = _.map(_.prop('dollar_value'), cars);
return _average(dollar_values);
};
averageDollarValue(CARS); // 790700
// Answer here
var averageDollarValue = _.pipe(
_.map(_.prop('dollar_value')),
_average
)
用 compistion style 寫一個 sanitizeNames()
函式,所有列表成員都轉小寫跟加底線e.g: sanitizeNames(["Hello World"]) //=> ["hello_world"]
// Answer here
const _underscore = _.replace(/\W+/g, '_'); //<-- leave this alone and use to sanitize
const sanitizeNames = _.pipe(
_.map(_.prop('name')),
_.map(_underscore),
_.map(_.toLower))
sanitizeNames(CARS);
//['ferrari_ff', 'spyker_c12_zagato', 'jaguar_xkr_s', 'audi_r8', 'aston_martin_one_77', 'pagani_huayra']);
// even better
const sanitizeNames = _.map(
_.pipe(
_.prop('name'),
_underscore,
_.toLower
)
)
const availablePrices = function(cars) {
const available_cars = _.filter(_.prop('in_stock'), cars);
return available_cars.map(x => formatMoney(x.dollar_value)).join(', ');
}
const formatDollarValue = _.pipe(
_.prop('dollar_value'),
formatMoney
)
availablePrices(CARS) // '$700,000.00, $1,850,000.00'
// Answer here
const availablePrices = _.pipe(
_.filter(_.prop('in_stock')),
_.map(formatDollarValue),
_.join(', ')
)
const fastestCar = function(cars) {
const sorted = _.sortBy(car => car.horsepower, cars);
const fastest = _.last(sorted);
return fastest.name + ' is the fastest';
}
fastestCar(CARS); // 'Aston Martin One-77 is the fastest'
// Answer here
const fastestCar = _.pipe(
_.sortBy(car => car.horsepower),
_.last,
_.prop('name'),
_.flip(_.concat)(' is the fastest')
)
原始碼 codepen 在這裡
你可以會有疑問說,那用 composition style 之後要怎麼除錯?平常很習慣用 console.log
看現在值是多少啊,其實可以像以下這樣做,就可以印出現在值
const concat = curry((y, x) => x + y);
const log = curry((tag, x) => (console.log(tag, x), x)
const shout = _.pipe(
concat('?'),
log(': here')
)
console.log(shout('hannah'))
// hannah?: here'
如有錯誤或需要改進的地方,拜託跟我說。
我會以最快速度修改,感謝您
歡迎追蹤我的部落格,除了技術文也會分享一些在矽谷工作的甘苦。