Javascript 和 haskell 之间的交互 (stdin/stdout)

interaction between Javascript and haskell (stdin/stdout)

提问人:fulverin 提问时间:3/20/2023 更新时间:3/24/2023 访问量:98

问:

我有一个练习要做,其中:

  • 我们需要用我们想要的任何语言编写一个程序(我选择了 Haskell), 该程序采用 stdin 并返回 stdout(以无限的 while 循环方式)。当我从终端尝试时,这部分工作正常
  • 然后提供的 JS 服务器必须调用我们编写的程序,提供输入并对其输出执行某些操作。(实际上 JS 服务器应该调用调用程序的 bash 脚本,但是,我可以绕过它)这部分不起作用(服务器在与 Golang 版本交互时工作正常,但它听起来对我的 Haskell 版本没有响应)

以下是 Haskell IO 循环的简化片段:

main = do
  guess_loop []

guess_loop xs = do
  x <- readLn
  let new_list = x:xs
  putStr $ show $ doSmothing new_list
  putStr "\n"
  guess_loop new_list

这是与我的程序通信的片段 JS(我认为):

  //executes the program with the numbers as stdin entries and saves its ouput in `output`
  let output
  try {
    output = execSync(test_file, {
      input: values_to_test,
    })
  } catch (err) {
    res.send({ status: 500, msg: 'No file found: ' + guesser })
    throw Error('No file found\n' + err)
  }

  let value = output.toString().split('\n')
  value.pop()

  let result = 0
  let returning = []
  let correct = 0

这是简化的工作 golang 版本片段

func main() {
    reader := bufio.NewScanner(os.Stdin)
    var k []float64
    for reader.Scan() {
        t := reader.Text()
        n, err := strconv.Atoi(t)
        k = append(k, float64(n))
        fmt.PrintLn(do_something(k)) // return []float64
    }
}

我对JS一无所知(也不需要),我知道一些golang,我正在学习haskell

实际上什么也没发生,所以没有错误可以分享。

我知道 haskell 关于 IO 的懒惰方面可能在起作用,所以我在 IO 循环(下面)之前添加了徒劳无功。hSetBuffering stdin LineBuffering hSetBuffering stdout LineBufferingmain = do

我试图将 console.log('hint') 放在 JS 代码的任何地方,以弄清楚发生了什么,但没有运气。我还制作了 haskell IO 循环以将输出写入文件,以检查它在 JS 调用时是否真的处于活动状态,并且确实如此。

我的直觉告诉我,这两者之间有些不对劲try { output = execSync(test_file,inputs}PutStr result

如果你能以任何方式帮助我:发现一个明显的错误或让我走上正轨;那太好了。理想情况下,我宁愿对 haskell 做点什么,而不触及 JS 的。 如果您有任何线索,请分享。 谢谢

JavaScript Haskell IO

评论

0赞 chi 3/20/2023
要查看这是否是缓冲问题,您可以尝试在等待输入的每个操作或类似操作之前运行。hFlush stdoutreadLn
0赞 leftaroundabout 3/20/2023
您确定简化版本中存在问题吗?看起来这应该与 Go 版本完全相同。(我不认为缓冲与问题有任何关系,因为您正在提供整个输入并读取整个输出。
0赞 fulverin 3/20/2023
谢谢@chi,我实际上尝试了那个,我没有提到它,因为我认为在循环之前或多或少与进入循环是一回事hSetBuffering stdout LineBufferinghFlush stdout
1赞 chi 3/20/2023
我看不出你的代码有什么问题,但可以肯定的是,我会这样写,以便为它提供一个显式的类型。(替换为预期的类型。我想知道是否推断出一些意想不到的类型。此外,当输入格式不正确时可能会崩溃,但我想您没有看到崩溃 (?let new_list :: [Int] ; new_list = x:xsIntreadLn
1赞 Daniel Wagner 3/21/2023
这可以使用一个最小的可重现示例(以及像我这样可能很了解 Haskell 但 Javascript 根本不知道如何运行它的人的说明)。

答:

4赞 K. A. Buhr 3/21/2023 #1

问题可能是您的示例程序永远读取输入,当它遇到文件末尾时,它会以错误终止。Golang 版本读取输入,直到遇到文件末尾,然后正常退出。

Javascript 驱动程序似乎提供有限数量的输入,并且它会响应程序成功运行的任何失败并出现错误,忽略在程序失败之前可能产生的任何输出。values_to_test500 No File Found

您可以尝试重写 Haskell 程序,以在循环开始时检查文件末尾:

import Control.Monad (when)
import System.IO (isEOF)

main = do
  guess_loop []

guess_loop xs = do
  eof <- isEOF
  when (not eof) $ do
    x <- readLn
    let new_list = x:xs
    putStr $ show $ doSomething new_list
    putStr "\n"
    guess_loop new_list

这可能看起来很尴尬,但随着你对 Haskell 的体验越来越丰富,你会发现编写这个程序的更自然的方式是这样的:

import Data.List

main = do
  input <- getContents                  -- read all of stdin, lazily
  let values = map read (lines input)   -- read one value per line
  let output = map doSomething (tail (inits values))
     -- run the computation on every initial part of the list
  putStr $ unlines (map show output)    -- write outputs one per line

评论

1赞 fulverin 3/21/2023
这行得通!我现在明白为什么了。非常感谢。“eof”和所有流的概念都是我选择忽略的事情的一部分,直到它打了我的脸。但是什么打了你一巴掌,可能并不总是告诉它的名字。
1赞 leftaroundabout 3/24/2023
@fulverin 正如答案底部所说,实际上很少明确使用。如果你能一口气读完整个输入,然后一口气写出输出(因为在这种情况下,JavaScript似乎无论如何都需要),那就容易多了。事实上,这可以通过 interact 进一步简化。eof
0赞 fulverin 3/27/2023
@leftaroundabout,在我的情况下,这是与“我的”程序交互的“其他”/JS 程序仅馈送一行,等待输出,然后(仅在)馈送另一行之后。练习的重点是逐步猜测/描述分布;其值是一个接一个地给出的,每次我都应该提供更好的猜测。一次获得所有行会扼杀练习的重点(我确实尝试过,但在这里并不容易获胜),但看起来交互可以使我的代码方式更漂亮,谢谢