[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' }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 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