π€ Chapter 9: λλ€ λΌμ΄λΈλ¬λ¦¬
- μμΈν λ΄μ©μ μ± μ μ°Έκ³ ν©μλΉ (P.199 ~ P.258)
π¦ λλ€ κΈ°λ³Έ μ¬μ©λ²β
π R.range ν¨μβ
import * as R from 'ramda';
console.log(R.range(1, 9 + 1)); // [1, 2, 3, 4, 5, 6, 7, 8, 9]
π R.tap λλ²κΉ μ© ν¨μβ
R.tap
ν¨μλ 2μ°¨ κ³ μ°¨ ν¨μ ννλ‘ νμ¬ κ°μ νμ ν μ μκ² ν΄μ€λ€.
import * as R from 'ramda';
const numbers: number[] = R.range(1, 9 + 1);
R.tap(n => console.log(n))(numbers); // [1, 2, 3, 4, 5, 6, 7, 8, 9]
π R.pipe ν¨μβ
import * as R from 'ramda';
const array: number[] = R.range(1, 10);
R.pipe(R.tap(n => console.log(n)))(array); // [1, 2, 3, 4, 5, 6, 7, 8, 9]
π ν¬μΈνΈκ° μλ ν¨μβ
import * as R from 'ramda';
const dump = <T>(array: T[]): T[] => R.pipe(
R.tap(n => console.log(n))
)(array) as T[]; // νμ
λ¨μΈ μ¬μ©
dump(R.range(1, 10)); // [1, 2, 3, 4, 5, 6, 7, 8, 9]
π μλ 컀리 μ΄ν΄νκΈ°β
import * as R from 'ramda';
console.log(
R.add(1, 2), // 3
R.add(1)(2), // 3
);
π R.curryN ν¨μβ
- Nκ°μ 맀κ°λ³μλ₯Ό κ°μ§ 1μ°¨ ν¨μ(first function)λ₯Ό Nκ°μ 컀리 맀κ°λ³μλ₯Ό κ°μ§λ Nμ°¨ κ³ μ°¨ ν¨μλ‘ λ§λ€μ΄ μ€λ€.
import * as R from 'ramda';
const sum = (...numbers: number[]): number =>
numbers.reduce((result: number, sum: number) => result + sum, 0);
const curriedSum = R.curryN(4, sum);
console.log(
curriedSum(), // [Function (anonymous)]
curriedSum(1), // [Function (anonymous)]
curriedSum(1)(2), // [Function (anonymous)]
curriedSum(1)(2)(3), // [Function (anonymous)]
curriedSum(1)(2)(3)(4), // 10
);
π μμ ν¨μβ
- λλ€ λΌμ΄λΈλ¬λ¦¬κ° μ 곡νλ ν¨μλ€μ νμ μ λ ₯ λ³μμ μνλ₯Ό λ³νμν€μ§ μκ³ μλ‘μ΄ κ°μ λ°ννλ€.
π¦ λ°°μ΄μ λ΄κΈ΄ μ λ€λ£¨κΈ°β
π μ μΈν νλ‘κ·Έλλ°β
- μ μΈν νλ‘κ·Έλλ°μμ λͺ¨λ μ λ ₯ λ°μ΄ν°λ λ€μμ²λΌ λ¨μ λ°μ΄ν°λ³΄λ€λ°°μ΄ ννλ₯Ό μ£Όλ‘ μ¬ μ©νλ€.
const value = 1;
const newArray = R.pipe(
R.map(R.inc)
)([value]) // [2]
R.pipe
μμμλconsole.log()
λ¬Έμ μ§μ μ¬μ©ν μ μμΌλ―λ‘ λ°λμR.tap
ν¨μλ₯Ό μ¬μ©ν΄μΌ νλ€.
π μ¬μΉ μ°μ° ν¨μβ
R.add(a: number)(b: number); // a + b
R.subtract(a: number)(b: number); // a - b
R.multiply(a: number)(b: number); // a * b
R.divide(a: number)(b: number); // a / b
π R.addIndex ν¨μβ
Array.map
μ λ λ²μ§Έ 맀κ°λ³μλ‘index
λ₯Ό μ 곡νμ§λ§,R.map
μArray.map
κ³Ό λ€λ₯΄κ²index
맀κ°λ³μλ₯Ό κΈ°λ³Έμ μΌλ‘ μ 곡νμ§ μλλ€. λ°λΌμR.addIndex
ν¨μλ₯Ό μ¬μ©ν΄R.map
μ΄index
λ₯Ό μ 곡νλ μλ‘μ΄ ν¨μλ₯Ό λ§λ€μ΄μΌ νλ€.
import * as R from 'ramda';
const addIndex = R.pipe(
R.addIndex(R.map)(R.add),
// R.addIndex(R.map)((value: number, index: number) => R.add(value)(index)),
R.tap(a => console.log(a)) // [1, 3, 5, 7, 9, 11, 13, 15, 17]
);
const newNumbers = addIndex(R.range(1, 9 + 1));
π R.flip ν¨μβ
- λλ€λ
R.flip
μ΄λΌλ ν¨μλ₯Ό μ 곡νλλ°R.flip
μR.subtract
μ κ°μ 2μ°¨ κ³ μ°¨ ν¨μ μ λ§€κ° λ³μ μμλ₯Ό λ°κΏμ€λ€.
import * as R from 'ramda';
const reverseSubtract = R.flip(R.subtract);
const newArray = R.pipe(
R.map(reverseSubtract(10)), // value - 10
R.tap(a => console.log(a)), // [ -9, -8, -7, -6, -5, -4, -3, -2, -1 ]
)(R.range(1, 9 + 1));
π μ¬μΉ μ°μ° ν¨μλ€μ μ‘°ν©β
f(x) = ax2 + bx + c
import * as R from 'ramda';
type NumberToNumberFunc = (number) => number;
// λλ€λ₯Ό μ¬μ©νμ§ μμ
//const f = (a: number, b: number, c: number): NumberToNumberFunc =>
// (x: number): number => a * x ** 2 + b * x + c;
// λλ€ ν¨μ μ¬μ©
const exp = (N: number) => (x: number) => x ** N;
const square = exp(2);
export const f = (a: number, b: number, c: number): NumberToNumberFunc =>
(x: number): number => R.add(
R.add(
R.multiply(a)(square(x))
)(R.multiply(b)(x)),
c
);
π¦ μμ μμ 쑰건 μ°μ°β
π μμ ν¬κΈ°λ₯Ό νλ¨νλ μμ μβ
- μλ₯Ό λΉκ΅ν΄
true
λfalse
λ₯Ό λ°ννλ λ€μμ μμ μλ€μ μ 곡νλ€.
R.lt(a)(b): boolean // a < b μ΄λ©΄ true, aκ° bλ³΄λ€ μμ
R.lte(a)(b): boolean // a <= b μ΄λ©΄ true, aκ° bλ³΄λ€ μκ±°λ κ°μ
R.gt(a)(b): boolean // a > b μ΄λ©΄ true, aκ° bλ³΄λ€ νΌ
R.gte(a)(b): boolean // a >= b μ΄λ©΄ true, aκ° bλ³΄λ€ ν¬κ±°λ κ°μ
- λ°°μ΄μ μμ΄ν μ€ 3λ³΄λ€ ν¬κ±°λ κ°μ μλ§ μ ν
import * as R from 'ramda';
R.pipe(
R.filter(R.lte(3)),
R.tap(n => console.log(n)) // [3, 4, 5, 6, 7, 8, 9, 10]
)(R.range(1, 10 + 1));
π R.allPass λ‘μ§ ν¨μβ
R.lt
,R.gt
μ²λΌboolean
νμ μ κ°μ λ°ννλ ν¨μλ€μR.allPass
μR.anyPass
λΌλ λ‘μ§ ν¨μλ₯Ό ν΅ν΄ κ²°ν©ν μ μλ€.
R.allPass(μμ μλ°°μ΄) // λ°°μ΄μ 쑰건μ λͺ¨λ λ§μ‘±νλ©΄ true
R.anyPass(μμ μλ°°μ΄) // λ°°μ΄μ μ‘° 건μ νλλΌλ λ§μ‘±νλ©΄ true
- λ€μ μλ
x
κ°min <= x < max
쑰건μ λ§μ‘±νλμ§R.allPass
ν¨μλ₯Ό μ¬μ©ν΄ νμΈνλ€.
import * as R from 'ramda';
type NumberToBooleanFunc = (n: number) => boolean;
export const selectRange = (min: number, max: number): NumberToBooleanFunc =>
R.allPass([
R.lte(min),
R.gt(max),
]);
π R.not ν¨μβ
- μ
λ ₯κ°μ΄
true
μ΄λ©΄false
λ₯Ό λ°ννκ³false
μ΄λ©΄true
λ₯Ό λ°ννλ ν¨μμ΄λ€. - μ΄μ μ ꡬνν
selectRange
μ λ°λλ‘ μμ©νλnotRange
λ₯Ό ꡬνν μ μλ€.
import * as R from 'ramda';
import { selectRange } from './selectRange';
export const notRange = (min: number, max: number) =>
R.pipe(selectRange(min, max), R.not);
π R.ifElse ν¨μβ
R.ifElse
ν¨μ λ μΈ κ°μ§ 맀κ°λ³μλ₯Ό ν¬ν¨νλλ°, 첫 λ²μ§Έλtrue/false
λ₯Ό λ°ννλ μμ μλ₯Ό, λ λ²μ§Έλ μ νμκ°true
λ₯Ό λ°νν λ μ€νν ν¨μλ₯Ό μΈ λ²μ§Έλ μ νμκ°false
λ₯Ό λ°νν λ μ€νν ν¨μμ΄λ€.- λ€μ μ½λλ 1λΆν° 10κΉμ§ μμμ μ€κ°κ° 6λ³΄λ€ μμ μλ 1μ© κ°μμν€κ³ , κ°κ±°λ ν° μλ 1μ© μ¦κ°μν€λ κ²μ ꡬνν μμ΄λ€.
import * as R from 'ramda';
const input: number[] = R.range(1, 10 + 1);
const halfVale = input[input.length / 2]; // 6
const subtractOrAdd = R.pipe(
R.map(R.ifElse(
R.lte(halfVale), // 쑰건 μμ μ: x => half <= x,
R.inc, // true μΌ λ μ€νν ν¨μ
R.dec, // false μΌ λ μ€νν ν¨μ
)),
R.tap(a => console.log(a)), // [0, 1, 2, 3, 4, 7, 8, 9, 10, 11]
);
const result = subtractOrAdd(input);
π¦ λ¬Έμμ΄ λ€λ£¨κΈ°β
- λ¬Έμμ΄ μλ€μ λ°±μ λ¬Έμ μλ₯΄κΈ°
import * as R from 'ramda';
R.trim('\t hello \n'); // hello
- λμλ¬Έμ μ ν
import * as R from 'ramda';
R.toUpper('Hello'); // HELLO
R.toLower('HELLO'); // hello
- λ¬Έμμ΄μ λ°°μ΄λ‘ λ³νκ³Ό λ°°μ΄μ λ¬Έμμ΄λ‘ λ³ν
import * as R from 'ramda';
const words: string[] = R.split(' ')(`Hello world!, I'm Peter.`);
// ['Hello', 'world!,', "I'm", 'Peter.']
R.join(' ')(words);
// "Hello world!, I'm Peter."
π¦ chance ν¨ν€μ§λ‘ κ°μ²΄ λ§λ€κΈ°β
- μμ λ₯Ό λ°λΌ ν΄λ΄. (P.225 ~ P.232)
src/model directory μ°Έκ³
π¦ λ μ¦λ₯Ό νμ©ν κ°μ²΄μ μμ± λ€λ£¨κΈ°β
π λ μ¦λ?β
- λ μ¦λ νμ€μΌ μΈμ΄μ
Control.Lens
λΌμ΄λΈλ¬λ¦¬ λ΄μ© μ€ μλ°μ€ν¬λ¦½νΈμμ λμν μ μλ κ²ν°μ μΈν° κΈ°λ₯λ§μ λλ€ ν¨μλ‘ κ΅¬νν κ²μ΄λ€. λλ€μ λ μ¦ κΈ°λ₯μ νμ©νλ©΄ κ°μ²΄μ μμ±κ°μ μ»κ±°λ μ€μ νλ λ±μ μμ μ μ½κ² ν μ μλ€.
R.lens
ν¨μλ‘ κ°μ²΄μ νΉμ μμ±μ λν λ μ¦λ₯Ό λ§λ λ€.- λ μ¦λ₯Ό
R.view
ν¨μμ μ μ©ν΄ μμ±κ°μ μ»λλ€.- λ μ¦λ₯Ό
R.set
ν¨μμ μ μ©ν΄ μμ±κ°μ΄ λ°λ μλ‘μ΄ κ°μ²΄λ₯Ό μ»λλ€.- λ μ¦μ μμ±κ°μ λ°κΎΈλ ν¨μλ₯Ό
R.over
ν¨μμ μ μ©ν΄ κ°μ΄ λ°λ μλ‘μ΄ κ°μ²΄λ₯Ό μ»λλ€.
π R.propκ³Ό R.assoc ν¨μβ
R.prop
λ κ°μ²΄μ νΉμ μμ±κ°μ κ°μ Έμ€λ ν¨μλ‘μ, μ΄λ° λμμ νλ ν¨μλ₯Ό κ²ν°λΌκ³ νλ€.
import * as R from 'ramda';
import { IPerson, makeRandomIPerson } from "./model/person";
const person: IPerson = makeRandomIPerson();
const name = R.pipe(
R.prop('name'),
R.tap(name => console.log(name)), // λλ€ μμ±λ μ΄λ¦
)(person);
- κ°μ²΄μ νΉμ μμ±κ°μ λ³κ²½νλ €λ©΄
R.assoc
ν¨μλ₯Ό μ¬μ©νλλ°, μ΄λ° λͺ©μ μΌλ‘ μ¬μ©νλ ν¨μλ₯Ό μΈν°λΌκ³ νλ€.
import * as R from 'ramda';
import { IPerson, makeRandomIPerson } from './model/person';
const getName = R.pipe(R.prop('name'), R.tap(name => console.log(name)));
const person: IPerson = makeRandomIPerson();
const originalName = getName(person); // λλ€ μμ±λ μ΄λ¦
const modifiedPerson = R.assoc('name', 'Seungmin')(person);
const modifiedName = getName(modifiedPerson); // Seungmin
π R.lens ν¨μβ
- λ μ¦λ λ€μμ²λΌ
R.lens
,R.prop
,R.assoc
μ μ‘°ν©μΌλ‘ λ§λ€ μ μλ€.
export const makeLens = (propName: string) => R.lens(R.prop(propName), R.assoc(propName));
π R.view, R.set, R.over ν¨μβ
R.view
,R.set
,R.over
ν¨μμ λ μ¦λ₯Ό μ μ©ν΄μ λ€μκ³Ό κ°μ κ²ν°μ μΈν° 그리κ³setterUsingFunc
κ³Ό κ°μ ν¨μλ₯Ό λ§λ€ μ μλ€.
import * as R from 'ramda';
export const makeLens = (propName: string) =>
R.lens(R.prop(propName), R.assoc(propName));
export const getter = (lens) => R.view(lens);
export const setter = (lens) => <T>(newValue: T) => R.set(lens, newValue);
export const setterUsingFunc = (lens) => <T, R>(func: (T) => R) => R.over(lens, func);
- μ μ½λλ₯Ό μ¬μ©ν
lens.ts
μ ν¨μλ€μ ν μ€νΈλlens-test.ts
λ₯Ό μ°Έκ³
π R.lensPath ν¨μβ
- λλ€ λΌμ΄λΈλ¬λ¦¬μμλ κ°μ²΄μ μ€μ²© μμ±μ κ²½λ‘(path)λΌκ³ νλ€.
longitude
μ²λΌ κΈ΄ κ²½λ‘μ μμ±μ λ μ¦λ‘ λ§λ€λ €λ©΄R.lensPath
ν¨μλ₯Ό μ¬μ©νλ€.
λ μ¦ = R.lensPath(['location', 'coordinates', 'longitude']);
- μ΄λ κ² λ μ¦λ₯Ό λ§λ€μμΌλ©΄ μμμ ꡬνν
lens.ts
μ κ²ν°μ μΈν° 그리κ³setterUsingFunc
ν¨μμ λ°λ‘ μ μ©ν μ μλ€.
import * as R from 'ramda';
import { getter, setter, setterUsingFunc } from './lens';
import { IPerson, makeRandomIPerson } from './model/person';
const longitudeLens = R.lensPath(['location', 'coordinates', 'longitude']);
const getLongitude = getter(longitudeLens);
const setLongitude = setter(longitudeLens);
const setLongitudeUsingFunc = setterUsingFunc(longitudeLens);
const person: IPerson = makeRandomIPerson();
const longitude = getLongitude(person);
const newPerson = setLongitude(0.1234567)(person);
const anotherPerson = setLongitudeUsingFunc(R.add(0.1234567))(person);
console.log(
longitude, getLongitude(newPerson), getLongitude(anotherPerson),
);
// 91.00362 0.1234567 91.1270767