将 getLine 输入与 haskell 数组连接会引发类型错误
concatenate getLine input with haskell array throws a type error
我完全是 Haskell 的初学者 我来自 js 环境,我有一个简单的数组 students
我想 push 一些学生对象但是遗憾的是 Haskell 不支持对象(如果有办法我可以做到,请指导我)所以尝试制作一个简单的程序来读取用户输入(数组)和 push 进入 students
数组,这是我尝试过的:
main :: IO()
main = do
let students = []
studentArray <- getLine
students ++ studentArray
print(students)
但抛出以下错误:Couldn't match type `[]' with `IO'
首先,您可能想看一下 this SO answer 中的资源。如果您还没有完成 "Absolute Beginner" 部分中的教程,那将是一个很好的起点。
请注意,对于其他编程语言,通常从在屏幕上打印 "Hello, world!"
的程序开始,或者像您的示例一样从控制台读取学生列表并将它们打印出来。对于 Haskell,一开始使用完全不同类型的程序通常更有意义。例如,教程 "Learn You a Haskell for Great Good" doesn't get to "Hello, world!"
until Chapter 9, and the "Happy Learn Haskell Tutorial" 直到第 15 章才涉及到它(然后它只涉及输出——输入直到第 20 章才出现)。
无论如何,回到你的例子。问题出在 students ++ studentArray
行。这是一个将空列表 students = []
与 studentArray
的值连接起来的表达式,studentArray
是 getLine
检索到的 String
。由于 String
只是一个字符列表,空列表只是空字符串,因此您正在编写 JavaScript 函数的粗略等效项:
function main() {
var students = "" // empty list is just empty string
var studentArray = readLineFromSomewhere()
students + studentArray // concatenate strings and throw away result
console.log(students) // print the empty string
}
在 JavaScript 中,这会 运行 并打印空字符串,因为 students + studentArray
行不执行任何操作。在 Haskell 中,这不会进行类型检查,因为 Haskell 期望此 do
块中的所有(非 let
)行都是 I/O 操作:
main :: IO () -- signature forces `do` block to be I/O
main = do
let students = [] -- "let" line is okay
studentArray <- getLine -- `getLine` is IO action
students ++ studentArray -- **NOT** IO action: it's a String AKA [Char]
print students -- `print students` is IO action
因为 students ++ studentArray
是 String
/ [Char]
/ 出现在 IO
do 块中的字符列表,Haskell 期望 IO something
但发现 [something]
,它抱怨列表类型 ([]
) 和 IO
不匹配。
但是,即使您可以解决此问题,也无济于事,因为与 JavaScript +
运算符不同,与 JavaScript push
方法不同, Haskell ++
运算符不修改其参数,因此 a ++ b
仅 returns a
和 b
的串联而不更改 a
或 b
.
这是 Haskell 的一个非常基本的方面,这使得它与大多数其他编程语言不同。默认情况下,Haskell 变量是不可变的。一旦它们在顶层被 let
语句赋值,或者在函数调用中作为参数赋值,它们的值就不会改变。 (事实上,因为它们不是真正的 "variable",我们通常称它们为 "bindings" 而不是 "variables"。)所以,如果你想建立一个 students
的列表在 Haskell 中,您不会首先将一个空列表分配给一个变量,然后尝试通过添加学生来修改该变量。相反,您要么一次完成所有操作:
import Control.Monad (replicateM)
main :: IO ()
main = do
putStrLn "Enter number of students:"
n <- readLn
putStrLn $ "Enter " ++ show n ++ " student names:"
students <- replicateM n getLine
putStrLn $ "List of students:"
print students
或使用函数调用通过将标识符重新绑定到更新后的值来模拟变量:
main :: IO ()
main = do
putStrLn "Enter number of students:"
n <- readLn
putStrLn $ "Enter " ++ show n ++ " student names:"
students <- getStudents n []
print students
getStudents :: Int -> [String] -> IO [String]
getStudents 0 studentsSoFar = return studentsSoFar
getStudents n studentsSoFar = do
student <- getLine
getStudents (n-1) (studentsSoFar ++ [student])
在这里查看 getStudents
最初是如何使用学生总数和初始空列表(在 [=43= 中分别绑定到 n
和 studentsSoFar
] 调用),然后使用递归重新绑定 n
和 studentsSoFar
以减少 n
而 "pushing" 更多学生到 studentsSoFar
.
表达式 studentsSoFar ++ [student]
本身不会做任何事情,但是通过在递归 getStudents
调用中使用它,这个新值可以重新绑定为 studentsSoFar
来模拟变化这个 "variable".
的值
无论如何,这在 Haskell 中是一种非常标准的方法,但对于来自 JavaScript 或其他语言的人来说可能不常见,因此值得在 [=88] 之前学习涵盖递归的教程=]...像"Learn You"(第5章的递归,第9章的I/O)或"Happy Learn"(第10章的递归,第15章和第20章的I/O)或"Haskell Programming from First Principles" (recursion in Chapter 8, I/O in Chapter 29) or "Programming in Haskell"(第六章递归,第十章I/O)。我确定您在这里看到了模式。
我完全是 Haskell 的初学者 我来自 js 环境,我有一个简单的数组 students
我想 push 一些学生对象但是遗憾的是 Haskell 不支持对象(如果有办法我可以做到,请指导我)所以尝试制作一个简单的程序来读取用户输入(数组)和 push 进入 students
数组,这是我尝试过的:
main :: IO()
main = do
let students = []
studentArray <- getLine
students ++ studentArray
print(students)
但抛出以下错误:Couldn't match type `[]' with `IO'
首先,您可能想看一下 this SO answer 中的资源。如果您还没有完成 "Absolute Beginner" 部分中的教程,那将是一个很好的起点。
请注意,对于其他编程语言,通常从在屏幕上打印 "Hello, world!"
的程序开始,或者像您的示例一样从控制台读取学生列表并将它们打印出来。对于 Haskell,一开始使用完全不同类型的程序通常更有意义。例如,教程 "Learn You a Haskell for Great Good" doesn't get to "Hello, world!"
until Chapter 9, and the "Happy Learn Haskell Tutorial" 直到第 15 章才涉及到它(然后它只涉及输出——输入直到第 20 章才出现)。
无论如何,回到你的例子。问题出在 students ++ studentArray
行。这是一个将空列表 students = []
与 studentArray
的值连接起来的表达式,studentArray
是 getLine
检索到的 String
。由于 String
只是一个字符列表,空列表只是空字符串,因此您正在编写 JavaScript 函数的粗略等效项:
function main() {
var students = "" // empty list is just empty string
var studentArray = readLineFromSomewhere()
students + studentArray // concatenate strings and throw away result
console.log(students) // print the empty string
}
在 JavaScript 中,这会 运行 并打印空字符串,因为 students + studentArray
行不执行任何操作。在 Haskell 中,这不会进行类型检查,因为 Haskell 期望此 do
块中的所有(非 let
)行都是 I/O 操作:
main :: IO () -- signature forces `do` block to be I/O
main = do
let students = [] -- "let" line is okay
studentArray <- getLine -- `getLine` is IO action
students ++ studentArray -- **NOT** IO action: it's a String AKA [Char]
print students -- `print students` is IO action
因为 students ++ studentArray
是 String
/ [Char]
/ 出现在 IO
do 块中的字符列表,Haskell 期望 IO something
但发现 [something]
,它抱怨列表类型 ([]
) 和 IO
不匹配。
但是,即使您可以解决此问题,也无济于事,因为与 JavaScript +
运算符不同,与 JavaScript push
方法不同, Haskell ++
运算符不修改其参数,因此 a ++ b
仅 returns a
和 b
的串联而不更改 a
或 b
.
这是 Haskell 的一个非常基本的方面,这使得它与大多数其他编程语言不同。默认情况下,Haskell 变量是不可变的。一旦它们在顶层被 let
语句赋值,或者在函数调用中作为参数赋值,它们的值就不会改变。 (事实上,因为它们不是真正的 "variable",我们通常称它们为 "bindings" 而不是 "variables"。)所以,如果你想建立一个 students
的列表在 Haskell 中,您不会首先将一个空列表分配给一个变量,然后尝试通过添加学生来修改该变量。相反,您要么一次完成所有操作:
import Control.Monad (replicateM)
main :: IO ()
main = do
putStrLn "Enter number of students:"
n <- readLn
putStrLn $ "Enter " ++ show n ++ " student names:"
students <- replicateM n getLine
putStrLn $ "List of students:"
print students
或使用函数调用通过将标识符重新绑定到更新后的值来模拟变量:
main :: IO ()
main = do
putStrLn "Enter number of students:"
n <- readLn
putStrLn $ "Enter " ++ show n ++ " student names:"
students <- getStudents n []
print students
getStudents :: Int -> [String] -> IO [String]
getStudents 0 studentsSoFar = return studentsSoFar
getStudents n studentsSoFar = do
student <- getLine
getStudents (n-1) (studentsSoFar ++ [student])
在这里查看 getStudents
最初是如何使用学生总数和初始空列表(在 [=43= 中分别绑定到 n
和 studentsSoFar
] 调用),然后使用递归重新绑定 n
和 studentsSoFar
以减少 n
而 "pushing" 更多学生到 studentsSoFar
.
表达式 studentsSoFar ++ [student]
本身不会做任何事情,但是通过在递归 getStudents
调用中使用它,这个新值可以重新绑定为 studentsSoFar
来模拟变化这个 "variable".
无论如何,这在 Haskell 中是一种非常标准的方法,但对于来自 JavaScript 或其他语言的人来说可能不常见,因此值得在 [=88] 之前学习涵盖递归的教程=]...像"Learn You"(第5章的递归,第9章的I/O)或"Happy Learn"(第10章的递归,第15章和第20章的I/O)或"Haskell Programming from First Principles" (recursion in Chapter 8, I/O in Chapter 29) or "Programming in Haskell"(第六章递归,第十章I/O)。我确定您在这里看到了模式。