[Functional Programming] Running though a serial number prediction functions for tagging, pairing the result into object

Let's we have some prediction functions, for each prediction function has a corresponding tag:

复制代码
const {gt, lte, gte, lt} = require('ramda');
const {flip} = require('crocks');

const getSmlPred = and(flip(gt, -1), flip(lte, 50));
const getMedPred = and(flip(gt, 50), flip(lte, 100));
const getLrgPred =  flip(gt, 100);
// taggedPred :: (b, (a -> Boolean))
// taggedPreds :: [taggedPred]
const taggedPreds = [
    ['Sml', getSmlPred],
    ['Med', getMedPred],
    ['Lrg', getLrgPred]
];
复制代码

So if we have input as:

15 -> 'Sml';
60 -> 'Med'
101 -> 'Lrg'

Also we wish our program to the safe, we want:

-1 -> Nothing

 

We can write a quick test function:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const {gt, lte, gte, lt} = require('ramda');
 
const {First, mreduceMap, and, safe, option, flip, objOf, assoc, not,
    merge, identity, fanout, curry, compose, map, constant} = require('crocks');
 
 
const getSmlPred = and(flip(gt, -1), flip(lte, 50));
const getMedPred = and(flip(gt, 50), flip(lte, 100));
const getLrgred =  flip(gt, 50);
// taggedPred :: (b, (a -> Boolean))
// taggedPreds :: [taggedPred]
const taggedPreds = [
    ['Sml', getSmlPred],
    ['Med', getMedPred],
    ['Lrg', getLrgred]
];
 
const fn = curry(([tag, pred]) => x => safe(pred)(x)
    .map(constant(tag)));
 
console.log('40 - Sml', fn(taggedPreds[0], 40)); // Just "Sml"
console.log('60 - Med', fn(taggedPreds[1], 60)); // Just "Med"
console.log('101 - Lrg', fn(taggedPreds[2], 101)); // Just "Lrg"

  

We can refactor the code into a more pointfree style and rename the testing 'fn' to 'tagValue':

复制代码
const {gt, lte, gte, lt} = require('ramda');

const {First, mreduceMap, and, safe, option, flip, objOf, assoc, not,
    merge, identity, fanout, curry, compose, map, constant} = require('crocks');


const getSmlPred = and(flip(gt, -1), flip(lte, 50));
const getMedPred = and(flip(gt, 50), flip(lte, 100));
const getLrgred =  flip(gt, 50);
// taggedPred :: (b, (a -> Boolean))
// taggedPreds :: [taggedPred]
const taggedPreds = [
    ['Sml', getSmlPred],
    ['Med', getMedPred],
    ['Lrg', getLrgred]
];
// tagValue :: taggedPred -> a -> Maybe b
const tagValue = curry(([tag, pred]) => compose(
    map(constant(tag)),
    safe(pred)
));    

console.log('40 - Sml', tagValue(taggedPreds[0], 40)); // Just "Sml"
console.log('60 - Med', tagValue(taggedPreds[1], 60)); // Just "Med"
console.log('101 - Lrg', tagValue(taggedPreds[2], 101)); // Just "Lrg"
console.log('-1 - Nothing', tagValue(taggedPreds[0], -5)); // Nothing "Nothing"
复制代码

 

Now, what we want is create fews functions, we know that we want data comes last, we want to partiaclly apply the predcitions functions.

// match :: [ taggedPreds ] -> a -> Maybe b
const match = 
    flip(x => mreduceMap(First, flip(tagValue, x)));  
const matchNumber = match(taggedPreds);
console.log('matchNumber', matchNumber(49)); // Just "Sml"

'mreduceMap': it take Monoid for combine the data together, here we use 'First', so only take the first Maybe result. 

Then for each [tag, pred], we will run with tagValue([tag, pred], x), because [tag, pred] will be partiaclly applied last, but 'tagValue' function takes it as first arguement, so we have to use 'flip'. And apply 'x' first.

In the test code above, we get ''Just Sml" back for number 49. It is good, but not enough, what we really want is keeping a Pair(a, s), for example: Pair('Sml', 40); we can keep the original state together with the tag result. 

 

What we can do is using 'fanout', which take two functions and generate a Pair:

const tagCard = fanout(compose(option(' | '), matchNumber), objOf('number'));
console.log('tagCard', tagCard(49)); // Pair( "Sml", { number: 49 } )

 

Lastly, we want to have the final result as:

const cardFromNumber = compose(
    merge(assoc('type')),
    tagCard
);

console.log(
    cardFromNumber(101)
) // { number: 101, type: 'Lrg' }

 

Full Code:

---

复制代码
const {gt, lte} = require('ramda');

const {First, mreduceMap, and, safe, option, flip, objOf, assoc,
    merge, fanout, curry, compose, map, constant} = require('crocks');


const getSmlPred = and(flip(gt, -1), flip(lte, 50));
const getMedPred = and(flip(gt, 50), flip(lte, 100));
const getLrgred =  flip(gt, 100);
// taggedPred :: (b, (a -> Boolean))
// taggedPreds :: [taggedPred]
const taggedPreds = [
    ['Sml', getSmlPred],
    ['Med', getMedPred],
    ['Lrg', getLrgred]
];

// tagValue :: taggedPred -> a -> Maybe b
const tagValue = curry(([tag, pred]) => compose(
    map(constant(tag)),
    safe(pred)
));    

// match :: [ taggedPreds ] -> a -> Maybe b
const match = 
    flip(x => mreduceMap(First, flip(tagValue, x)));  
const matchNumber = match(taggedPreds);

const tagCard = fanout(compose(option(' | '), matchNumber), objOf('number'));

const cardFromNumber = compose(
    merge(assoc('type')),
    tagCard
)

console.log(
    cardFromNumber(101)
) // { number: 101, type: 'Lrg' }
复制代码

 

---

This coding partten is useful, because we can keep the structure, just replace the tags and predications functions:

复制代码
// data.js
const {test} = require('ramda');

module.exports = [
    ['Diners - Carte Blanche|diners', test(/^30[0-5]/)],
    ['Diners|diners', test(/^(30[6-9]|36|38)/)],
    ['JCB|jcb', test(/^32(2[89]|[3-8][0-9])/)],
    ['AMEX|american-express', test(/^3[47]/)],
    ['Visa Electron|visa', test(/^(4026|417500|4508|4844|491(3|7))/)]
]
复制代码
复制代码
const {gt, lte} = require('ramda');

const {First, mreduceMap, and, safe, option, flip, objOf, assoc,
    merge, fanout, curry, compose, map, constant} = require('crocks');


const getSmlPred = and(flip(gt, -1), flip(lte, 50));
const getMedPred = and(flip(gt, 50), flip(lte, 100));
const getLrgred =  flip(gt, 100);
// taggedPred :: (b, (a -> Boolean))
// taggedPreds :: [taggedPred]
const taggedPreds = require('./data.js');
// tagValue :: taggedPred -> a -> Maybe b
const tagValue = curry(([tag, pred]) => compose(
    map(constant(tag)),
    safe(pred)
));    

// match :: [ taggedPreds ] -> a -> Maybe b
const match = 
    flip(x => mreduceMap(First, flip(tagValue, x)));  
const matchNumber = match(taggedPreds);

const tagCard = fanout(compose(option(' | '), matchNumber), objOf('number'));

const cardFromNumber = compose(
    merge(assoc('type')),
    tagCard
)

console.log(
    cardFromNumber('4026-xxxx-xxxxx-xxxx')
) // { number: '4026-xxxx-xxxxx-xxxx', type: 'Visa Electron|visa' }
复制代码

 

posted @   Zhentiw  阅读(264)  评论(0编辑  收藏  举报
编辑推荐:
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
历史上的今天:
2018-03-19 [Ionic] Align and Size Text with Ionic CSS Utilities
2017-03-19 [React Router v4] Use the React Router v4 Link Component for Navigation Between Routes
2017-03-19 [React Router v4] Create Basic Routes with the React Router v4 BrowserRouter
2015-03-19 [Javascript] Drawing Styles on HTML5 Canvas
2015-03-19 [Javascript] Drawing Paths - Curves and Arcs
2015-03-19 [Javascript] Drawing Paths - Lines and Rectangles
2015-03-19 [Whole Web] [AngularJS + Grunt] Using ng-html2js to Convert Templates into JavaScript
点击右上角即可分享
微信分享提示