iT邦幫忙

第 12 屆 iT 邦幫忙鐵人賽

DAY 18
0
Software Development

Functional Programming in JS系列 第 18

[練習] Composition Exercise

https://ithelp.ithome.com.tw/upload/images/20200918/20106426GAvL4clU8K.jpg

這一篇的解答會盡量用 Ramda.js 因為會變很簡潔,若還不熟可以看我上一篇[補充] Ramda.js

Practice 1

把以下改成 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'

Practice 2

這裡有一系列改寫成 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}
];

Q: isLastInStock

_.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
)

Q: nameOfFirstCar

_.compose()_.prop()_.head() 來找第一部車子的名稱

// Answer here
 const nameOfFirstCar = _.pipe(
  _.head,
  _.prop('name')
)

nameOfFirstCar(CARS); // "Ferrari FF"

Q: averageDollarValue

_.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
)

Q: sanitizeNames

用 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
  )
)

Q: Refactor availablePrices with compose

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(', ')
)

Q: Refactor fastestCar with compose

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'

參考文章

如有錯誤或需要改進的地方,拜託跟我說。
我會以最快速度修改,感謝您

歡迎追蹤我的部落格,除了技術文也會分享一些在矽谷工作的甘苦。


上一篇
[補充] Ramda.js
下一篇
Pointfree
系列文
Functional Programming in JS30

尚未有邦友留言

立即登入留言