zoukankan      html  css  js  c++  java
  • [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:

    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' }
  • 相关阅读:
    jQuery Validate 验证成功时的提示信息
    MySQL定时任务实现方法
    tp5获取器的用法。
    使用layui异步请求上传图片在tp5.1环境下出现“请对上传接口返回json”的错误的解决方法
    微信小程序底部菜单栏的使用方法
    接口测试中的接口到底是什么?
    【web自动化测试】requests-html 这个解析库,能让你更轻松的获取网页内容
    一个软件测试小白的进阶之路
    Python进阶:@property 动态属性
    百度网盘限速怎么办?
  • 原文地址:https://www.cnblogs.com/Answer1215/p/10561400.html
Copyright © 2011-2022 走看看