用Python生成和运行Haskell代码

3
我们正在编写一个Python程序,旨在根据输入输出对合成(简单的)Haskell函数。在程序运行期间,我们生成Haskell代码并针对用户提供的示例检查其正确性。
假设我们的输入为“1 2”,期望输出为“3”。我们最终会得到加法函数。然后我们将在Haskell中运行(\x y -> x + y) 1 2并检查它是否计算出3。
我们当前的做法是运行以下Python代码:
from subprocess import Popen, PIPE, STDOUT
proccess = Popen(f'ghc -e "{haskell_code}"', shell=True, stdout=PIPE, stderr=STDOUT) 
haskell_output = proc.stdout.read().decode('utf-8').strip('\n')

由于我们都不熟悉 GHC、Haskell、进程,或者与这些相关的任何内容,因此我们希望有人能够帮助我们以(更)高效的方式执行此任务,因为当前速度非常慢。

此外,我们希望能够执行多个语句。例如,我们想要导入 Data.Char 以便我们的函数可以使用“toUpper”。然而,我们目前的做法是发送一个单独的 lambda 函数和附加在其后的输入,我们不确定如何在其上添加导入语句(添加"\n"似乎没有起作用)。

总之,我们希望找到运行速度最快的解决方案,使我们能够从 Python 中测试 Haskell 函数(其中我们不预先拥有所有 Haskell 函数的代码,而是在生成代码时进行测试),同时允许我们使用多个语句(例如导入)。

如果其中任何内容都很简单或愚蠢,请谅解,我们非常感谢任何的帮助。


如果我没记错,ghc -e 是相当受限制的。你可能只能够使用完全合格化的 Data.Char.toUpper 或编写一个完整文件并用 runghc 执行它。 - melpomene
使用Data.Char.toUpper似乎可以解决第二个问题。然而,这将要求我们使用完全限定的名称而不是简单地使用"toUpper"。使用runghc是否比ghc -e更快?非常感谢您的快速回复! - CompareTwo
1个回答

6

这似乎是一件奇怪的事情...但仍然 有趣

这里立即出现了两个想法。首先是使用 ghci repl 而不是为每个 eval 尝试生成一个新进程。思路是将您的 I/O 流导入 ghci 进程,而不是为每次尝试生成一个新的 ghc 进程。为每个 eval 启动一个新进程的开销似乎对性能有很大影响。我通常会选择expect,但由于你需要 Python,我会用到pexpect

import pexpect
import sys
from subprocess import Popen, PIPE, STDOUT
import time


REPL_PS = unicode('Prelude> ')
LOOPS = 100


def time_function(func):
    def decorator(*args, **kwargs):
        ts = time.time()
        func(*args, **kwargs)
        te = time.time()
        print "total time", (te - ts)
    return decorator


@time_function
def repl_loop():
    repl = pexpect.spawnu('ghci')
    repl.expect(REPL_PS)
    for i in range(LOOPS):
        repl.sendline('''(\\x y -> x + y) 1 2''')
        _, haskell_output = repl.readline(), repl.readline()
        repl.expect(REPL_PS)


@time_function
def subproc_loop():
    for i in range(LOOPS):
        proc = Popen('''ghc -e "(\\x y -> x + y) 1 2"''', shell=True, stdout=PIPE, stderr=STDOUT) 
        haskell_output = proc.stdout.read().decode('utf-8').strip('n')
        # print haskell_output


repl_loop()
subproc_loop()

这给我提供了一个非常稳定的 >2x 速度提升。
有关更多信息,请参阅pexpect文档:https://github.com/pexpect/pexpect/ 第二个立即想到的想法是使用一些分布式计算方法。我没有时间在这里构建完整的演示,但在互联网和SO的领域中已经存在许多很好的例子。这个想法是有多个“python + ghci”进程从公共队列读取 eval 尝试,然后将结果推送到公共的 eval 尝试检查器。我不太了解ghc(i),但快速检查显示ghci是一个多线程进程,因此可能需要多台机器并行尝试不同的尝试子集。
这里可能会有一些有趣的链接: 如何在Python中使用多进程队列? https://docs.python.org/2/library/multiprocessing.html https://eli.thegreenplace.net/2012/01/24/distributed-computing-in-python-with-multiprocessing

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接