jjzjj

python - 结合 maybe 和 seq monads : confused at the output

coder 2023-08-17 原文

我正在尝试组合 seq-m 和 error-m 来对可能返回错误的事物进行列表推导。我的输出有意想不到的类型,但除此之外它实际上似乎是合理的。我在下面分解了我的代码,但这里有一个 working gist

这是我的 monadic 业务逻辑

def get_loan(name):
    m_qualified_amounts = (
           bind(get_banks(name), lambda bank:
           bind(get_accounts(bank, name), lambda account:
           bind(get_balance(bank, account), lambda balance:
           bind(get_qualified_amount(balance), lambda qualified_amount:
                    unit(qualified_amount))))))
    return m_qualified_amounts

names = ["Irek", "John", "Alex", "Fred"]
for name, loans in zip(names, map(get_loan, names)):
    print "%s: %s" % (name, loans)

输出

Irek: [None, 'Insufficient funds for loan, current balance is 35000', None, 'Insufficient funds for loan, current balance is 70000', None, 'Unable to get balance due to technical issue for Wells Fargo: 3']
John: [None, 'Insufficient funds for loan, current balance is 140000']
Alex: [[245000], None, [280000], None]
Fred: (None, 'No bank associated with name Fred')

我希望看到元组列表——该列表是列表推导的结果,最终列表中的每一项都应该是错误单子(monad)(value, error 元组)中的一个值。就好像 seq_bind 移除了太多层级的嵌套。

这是我对 monad 的定义,如果它不正确,它非常接近,因为两个 monad 都是独立工作的,只是没有结合。

def success(val): return val, None
def error(why): return None, why
def get_value(m_val): return m_val[0]
def get_error(m_val): return m_val[1]

# error monad
def error_unit(x): return success(x)
def error_bind(mval, mf):
    assert isinstance(mval, tuple)
    error = get_error(mval)
    if error: return mval
    else: return mf(get_value(mval))

def flatten(listOfLists):
    "Flatten one level of nesting"
    return [x for sublist in listOfLists for x in sublist]    

# sequence monad
def seq_unit(x): return [x]
def seq_bind(mval, mf):
    assert isinstance(mval, list)
    return flatten(map(mf, mval))

# combined monad !!
def unit(x): return error_unit(seq_unit(x))
def bind(m_error_val, mf):  
    return error_bind(m_error_val, lambda m_seq_val: seq_bind(m_seq_val, mf))

单一API

def get_banks(name):
    if name == "Irek": return success(["Bank of America", "Wells Fargo"])
    elif name == "John": return success(["PNC Bank"])
    elif name == "Alex": return success(["TD Bank"])
    else: return error("No bank associated with name %s" % name)

def get_accounts(bank, name):
    if   name == "Irek" and bank == "Bank of America": return success([1, 2])
    elif name == "Irek" and bank == "Wells Fargo": return success([3])
    elif name == "John" and bank == "PNC Bank": return success([4])
    elif name == "John" and bank == "Wells Fargo": return success([5, 6])
    elif name == "Alex" and bank == "TD Bank": return success([7, 8])
    else: return error("No account associated with (%s, %s)" % (bank, name))

def get_balance(bank, account):
    if bank == "Wells Fargo":
        return error("Unable to get balance due to technical issue for %s: %s" % (bank, account))
    else:
        return success([account * 35000])  #right around 200,000 depending on acct number

def get_qualified_amount(balance):
    if balance > 200000:
        return success([balance])
    else:
        return error("Insufficient funds for loan, current balance is %s" % balance)

也在寻找改进代码的方法。标记为 haskell 和 clojure,因为这在这些语言中是惯用的,python 社区对此不感兴趣。

最佳答案

在 Haskell 中使用 Monad Transformers 像这样通过堆叠组合 monad .暂时搁置 Daniel Wagner 的 ListT 不是 monad 的观点。您有两个具有类型的 monad:

  1. 列出一个,它看起来像[x,y,z]
  2. (Error e) a 看起来是 x, NoneNone, err

如果将一个转换为 monad 转换器并组合它们,有两种方法:

  1. (ErrorT e) 列出一个,看起来像 [ (x,None), (y,None), (None, err) ]
  2. ListT (ErrorT e) a 看起来像 [x,y,z], NoneNone, [x,y,z]

你想要一个对列表,所以我想你想要第一个形式。但是您的简单测试不同意这一点。您的 unit 不会返回 (1.) 中的成对列表,而是返回一对列表和 None,即 (2.)。

所以你要么有倒退的东西,要么你有一个更复杂的单子(monad)。我会尝试将您的要点修改为 (1.)。

我认为这段代码可能会做你想做的事:

def flatten(listOfLists):
    "Flatten one level of nesting"
    assert isinstance(listOfLists, list)
    if len(listOfLists) > 0:
        assert isinstance(listOfLists[0], list)
    return [x for sublist in listOfLists for x in sublist]

# sequence monad
def seq_unit(x): return [x]
def seq_bind(mval, mf): return flatten(map(mf, mval))

# Decompose ErrorT e m a
def get_value(m_val): return m_val[0]
def get_error(m_val): return m_val[1]

# hard coded "(ErrorT e) List a" instance of throwError, note that seq_unit is hardcoded
def error_throwError(err): return (None, err)
def errorT_list_throwError(err): return seq_unit(error_throwError(err))

# "(ErrorT e) List a" monad
def error_unit(x): return (x,None)
def errorT_list_unit(x): return seq_unit(error_unit(x))

def error_bind(mval, mf):
    assert isinstance(mval, tuple)
    error = get_error(mval)
    if error:
        return error_throwError(error)
    else: 
        return mf(get_value(mval))

# Cannot have multi-line lambda
def errorT_list_bind_helper(mval, mf):
    assert isinstance(mval, tuple)
    error = get_error(mval)
    if error:
        return errorT_list_throwError(error)
    else: 
        return mf(get_value(mval))

def errorT_list_bind(mval, mf): return seq_bind(mval, lambda v: errorT_list_bind_helper(v, mf))

# combined monad !! (ErrorT e) List a
unit = errorT_list_unit
bind = errorT_list_bind
throwError = errorT_list_throwError

# hard coded "lift :: List a -> (ErrorT e) List a"
def lift(mval):
    assert isinstance(mval, list)
    # return [ (val,None) for val in mval ]
    # return [ errorT_list_unit(val) for val in mval ]
    return seq_bind(mval, lambda v : unit(v))

def get_banks(name):
    if name == "Irek": return lift(["Bank of America", "Wells Fargo"])
    elif name == "John": return unit("PNC Bank")
    elif name == "Alex": return unit("TD Bank")
    else: return throwError("No bank associated with name %s" % name)

def get_accounts(bank, name):
    if   name == "Irek" and bank == "Bank of America": return lift([1, 2])
    elif name == "Irek" and bank == "Wells Fargo": return unit(3)
    elif name == "John" and bank == "PNC Bank": return unit(4)
    elif name == "John" and bank == "Wells Fargo": return lift([5, 6])
    elif name == "Alex" and bank == "TD Bank": return lift([7, 8])
    else: return throwError("No account associated with (%s, %s)" % (bank, name))

def get_balance(bank, account):
    if bank == "Wells Fargo":
        return throwError("Unable to get balance due to technical issue for %s: %s" % (bank, account))
    else:
        return unit(account * 35000)  #right around 200,000 depending on acct number

def get_qualified_amount(balance):
    if balance > 200000:
        return unit(balance)
    else:
        return throwError("Insufficient funds for loan, current balance is %s" % balance)

# monadic business logic
def get_loan(name):

    m_qualified_amounts = (
           bind(get_banks(name), lambda bank:
           bind(get_accounts(bank, name), lambda account:
           bind(get_balance(bank, account), lambda balance:
           bind(get_qualified_amount(balance), lambda qualified_amount:
                    unit(qualified_amount))))))

    assert isinstance(m_qualified_amounts, list)
    assert isinstance(m_qualified_amounts[0], tuple)
    return m_qualified_amounts

names = ["Irek", "John", "Alex", "Fred"]

for name, loans in zip(names, map(get_loan, names)):
    print "%s: %s" % (name, loans)

输出是

Irek: [(None, 'Insufficient funds for loan, current balance is 35000'), (None, 'Insufficient funds for loan, current balance is 70000'), (None, 'Unable to get balance due to technical issue for Wells Fargo: 3')]
John: [(None, 'Insufficient funds for loan, current balance is 140000')]
Alex: [(245000, None), (280000, None)]
Fred: [(None, 'No bank associated with name Fred')]

关于python - 结合 maybe 和 seq monads : confused at the output,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10059163/

有关python - 结合 maybe 和 seq monads : confused at the output的更多相关文章

  1. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  2. ruby-on-rails - 结合 meta_search 与 acts_as_taggable_on - 2

    我在开发的Rails3网站的一些搜索功能上遇到了一个小问题。我有一个简单的Post模型,如下所示:classPost我正在使用acts_as_taggable_on来更轻松地向我的帖子添加标签。当我有一个标记为“rails”的帖子并执行以下操作时,一切正常:@posts=Post.tagged_with("rails")问题是,我还想搜索帖子的标题。当我有一篇标题为“Helloworld”并标记为“rails”的帖子时,我希望能够通过搜索“hello”或“rails”来找到这篇帖子。因此,我希望标题列的LIKE语句与acts_as_taggable_on提供的tagged_with方法

  3. Python 相当于 Perl/Ruby ||= - 2

    这个问题在这里已经有了答案:关闭10年前。PossibleDuplicate:Pythonconditionalassignmentoperator对于这样一个简单的问题表示歉意,但是谷歌搜索||=并不是很有帮助;)Python中是否有与Ruby和Perl中的||=语句等效的语句?例如:foo="hey"foo||="what"#assignfooifit'sundefined#fooisstill"hey"bar||="yeah"#baris"yeah"另外,类似这样的东西的通用术语是什么?条件分配是我的第一个猜测,但Wikipediapage跟我想的不太一样。

  4. java - 什么相当于 ruby​​ 的 rack 或 python 的 Java wsgi? - 2

    什么是ruby​​的rack或python的Java的wsgi?还有一个路由库。 最佳答案 来自Python标准PEP333:Bycontrast,althoughJavahasjustasmanywebapplicationframeworksavailable,Java's"servlet"APImakesitpossibleforapplicationswrittenwithanyJavawebapplicationframeworktoruninanywebserverthatsupportstheservletAPI.ht

  5. 华为OD机试用Python实现 -【明明的随机数】 2023Q1A - 2

    华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o

  6. python - 如何读取 MIDI 文件、更改其乐器并将其写回? - 2

    我想解析一个已经存在的.mid文件,改变它的乐器,例如从“acousticgrandpiano”到“violin”,然后将它保存回去或作为另一个.mid文件。根据我在文档中看到的内容,该乐器通过program_change或patch_change指令进行了更改,但我找不到任何在已经存在的MIDI文件中执行此操作的库.他们似乎都只支持从头开始创建的MIDI文件。 最佳答案 MIDIpackage会为您完成此操作,但具体方法取决于midi文件的原始内容。一个MIDI文件由一个或多个音轨组成,每个音轨是十六个channel中任何一个上的

  7. 「Python|Selenium|场景案例」如何定位iframe中的元素? - 2

    本文主要介绍在使用Selenium进行自动化测试或者任务时,对于使用了iframe的页面,如何定位iframe中的元素文章目录场景描述解决方案具体代码场景描述当我们在使用Selenium进行自动化测试的时候,可能会遇到一些界面或者窗体是使用HTML的iframe标签进行承载的。对于iframe中的标签,如果直接查找是无法找到的,会抛出没有找到元素的异常。比如近在咫尺的例子就是,CSDN的登录窗体就是使用的iframe,大家可以尝试通过F12开发者模式查看到的tag_name,class_name,id或者xpath来定位中的页面元素,会抛出NoSuchElementException异常。解决

  8. python ffmpeg 使用 pyav 转换 一组图像 到 视频 - 2

    2022/8/4更新支持加入水印水印必须包含透明图像,并且水印图像大小要等于原图像的大小pythonconvert_image_to_video.py-f30-mwatermark.pngim_dirout.mkv2022/6/21更新让命令行参数更加易用新的命令行使用方法pythonconvert_image_to_video.py-f30im_dirout.mkvFFMPEG命令行转换一组JPG图像到视频时,是将这组图像视为MJPG流。我需要转换一组PNG图像到视频,FFMPEG就不认了。pyav内置了ffmpeg库,不需要系统带有ffmpeg工具因此我使用ffmpeg的python包装p

  9. Python 刷Leetcode题库,顺带学英语单词(31) - 2

    ValidPalindromeGivenastring,determineifitisapalindrome,consideringonlyalphanumericcharactersandignoringcases. [#125]Example:"Aman,aplan,acanal:Panama"isapalindrome."raceacar"isnotapalindrome.Haveyouconsiderthatthestringmightbeempty?Thisisagoodquestiontoaskduringaninterview.Forthepurposeofthisproblem

  10. python - 是否可以使用 Ruby 或 Python 禁用 anchor /引用来发出有效的 YAML? - 2

    是否可以在PyYAML或Ruby的Psych引擎中禁用创建anchor和引用(并有效地显式列出冗余数据)?也许我在网上搜索时遗漏了一些东西,但在Psych中似乎没有太多可用的选项,而且我也无法确定PyYAML是否允许这样做.基本原理是我必须序列化一些数据并将其以可读的形式传递给一个不是真正的技术同事进行手动验证。有些数据是多余的,但我需要以最明确的方式列出它们以提高可读性(anchor和引用是提高效率的好概念,但不是人类可读性)。Ruby和Python是我选择的工具,但如果有其他一些相当简单的方法来“展开”YAML文档,它可能就可以了。 最佳答案

随机推荐