寻找一个惯用的,优雅的解决方案“发送+更多=钱”在伊利西

2024-10-03 02:47:28 发布

您现在位置:Python中文网/ 问答频道 /正文

我遇到了Mark Dominus' blog post,它描述了在Python中使用函数式编程技术(尤其是monad)来解决{a2}。在

这里总结了在死链接情况下的难题:

    S E N D   | Find each character's *unique* numerical value, such that
+   M O R E   | the addition on the left is valid. There are no leading zeros.
-----------
= M O N E Y

我一直在寻找机会学习一些纯函数式编程,特别是使用Elixir,这似乎是一个非常适合的项目。在

我可以用Elixir实现一个与Mark Dominus的Python代码非常相似的版本:

^{pr2}$

但有些东西告诉我,必须有一种方法来绕过疯狂嵌套的匿名函数和随后的表翻转;这就是为什么存在纯函数语言,对吧?在

看看马克·多米努斯的previous blog post in which he solves the puzzle with Haskell,我看到他用了哈斯凯尔的“绑定”操作符>>=的甜言蜜语来消除翻表的冲动。。。但是我没有learned me a Haskell,所以我对这篇博文中的代码没有很好的理解。在

我相当确定我在Elixir实现中缺少的是使用管道操作符|>,这对我来说是语言的一大吸引力(我自己对Unix管道非常熟悉)。我尝试过在管道中结合许多不同的Enum.{map,reduce}工作,但我总是回到起点。在

有人能给点建议吗?在理想的情况下,我在寻找一个功能性的解决方案。在


Tags: the函数代码语言a2管道haskell情况
2条回答

Haskell通过两个因素消除了这种语法干扰:操作符的关联性和lambdas的一个很好的语言规则。在

使用associativity rules可以消除语法中不必要的括号。例如,在Elixir中,加法与左边相关联,所以当你写a + 2 + x时,它被解释为(a + 2) + x。关联性规则允许您去掉括号。如果您的意思是a + (2 + x),那么您必须显式地编写它。在

你可以使用操作符的关联性来帮助解决长生不老的问题,有些人已经有了。MonadEx库定义了一个绑定运算符~>>,它可以让您将程序的内容编写为

    Smm.remove(digits, [0])
    ~>> fn s -> Smm.remove(digits, [s])
    ~>> fn e -> Smm.remove(digits, [s,e])
    ~>> fn n -> Smm.remove(digits, [s,e,n])
    ~>> fn d -> return(Smm.to_number([s,e,n,d]))
    ~>> fn w_send -> Smm.remove(digits, [0,s,e,n,d])
    ~>> fn m -> Smm.remove(digits, [s,e,n,d,m])
    ~>> fn o -> Smm.remove(digits, [s,e,n,d,m,o])
    ~>> fn r -> return(Smm.to_number([m,o,r,e]))
    ~>> fn w_more -> Smm.remove(digits, [s,e,n,d,m,o,r])
    ~>> fn y -> return(Smm.to_number([m,o,n,e,y]))
    ~>> fn w_money -> Smm.guard(w_send + w_more == w_money)
    ~>> fn -> return([w_send, w_more, w_money])
    end end end end end end end end end end end end

运算符关联性并不能消除lambda表达式都以同一个位置结束。后面的表达式需要在前面的lambdas中,这样它们才能看到前面引入的变量。Haskell通过一个简单的语法规则"lambda abstractions ... extend as far to the right as possible"来消除这种干扰。因为lambdas一直向右扩展,所以用相同样式编写的Haskell代码没有一大堆结束括号。在

^{pr2}$

我无法想象长生不老药有什么相应的诀窍。每一个fn都必须在末尾end,因此{}的数量将与绑定的数量相同。我想你只需要不断地翻表(╯°□°)╯︵ ┻━┻。在

您可以在这里查看:What is the "|>" symbol's purpose in Elixir?以获得|>运算符的概述。但基本思想是a |> f(b, c)与{}相同。当您执行类似a |> f(b) |> g(c)的操作时,这是非常有用的,根据上面的规则,它与g(f(a, b), c)相同,但是读起来更漂亮。在

也就是说,|>运算符(称为管道)不是一元绑定(>>=),并且不允许像>>=那样“展平”深度嵌套的循环。对于在长生不老药中看起来更好的替代方法,您可以:

  1. 使用实现monad语法的库(或添加您自己的),如MonadEx
  2. 停止使用这种循环方法,例如,使用递归函数预先为字母生成数字赋值,如下所示:

    defmodule Smm do
      # some more things
    
      def assignments(0, _), do: [[]]
      def assignments(n, digits \\ Enum.into(0..9, HashSet.new)) do
        digits
        |> Stream.flat_map(fn (d) ->
          for rest <- assignments(n - 1, Set.delete(digits, d)) do
            [d | rest]
          end
        end)
      end
    end
    
    for [s, e, n, d, m, o, r, y] <- Smm.assignments(8) do
      w_send = Smm.to_number([s, e, n, d])
      w_more = Smm.to_number([m, o, r, e])
      w_money = Smm.to_number([m, o, n, e, y])
    
      if s > 0 && m > 0 && (w_send + w_more == w_money) do
        IO.inspect([w_send, w_more, w_money])
      end
    end
    

相关问题 更多 >