为何在Elixir中无法向列表正确应用匿名函数?
问题分析与解决方案
嘿,这个问题的根源其实是两个容易忽略的Elixir细节:匿名函数的返回值类型,以及IO.puts/1处理列表的方式。让我一步步给你拆解清楚:
核心问题:整数被当作Unicode控制字符处理
你的fizz_buzz匿名函数在最后一个分支(_, _, c) -> c返回的是原始整数n,而不是字符串。当你单独调用IO.puts(fizz_buzz_rem.(11))时,IO.puts/1会自动把整数转换成字符串输出,所以能看到正常的11;但当你把包含整数和字符串的列表传给IO.puts/1时,情况就变了:
Elixir会把列表视为iodata(一种高效表示二进制数据的结构),此时列表中的整数会被解析为Unicode码点,而不是转换成字符串。你用到的这些整数对应的都是终端控制字符:
11:垂直制表符(VT),会让光标跳转到下一行的同一列13:回车符(CR),会把光标移到当前行的开头14:换页符(FF),会清空终端屏幕或滚动到新页面16、17:设备控制字符,完全不可见
这些控制字符会彻底打乱终端的输出逻辑,导致你只能看到没有被干扰的字符串(比如开头的"Buzz"和最后的"FizzBuzz"),中间的数字都被控制字符的效果覆盖了。
解决方案:统一返回字符串类型
首先修改fizz_buzz的最后一个分支,把整数转为字符串:
fizz_buzz = fn (0, 0, _) -> "FizzBuzz" (0, _, _) -> "Fizz" (_, 0, _) -> "Buzz" (_, _, c) -> to_string(c) # 这里把整数转为字符串,统一返回类型 end
优化输出方式:用Enum.each实现逐行打印
即使统一了返回类型,直接把map后的列表传给IO.puts也会把所有字符串拼接成一行输出。如果你想要每行一个结果的效果,应该用Enum.each遍历列表,对每个元素单独调用IO.puts/1:
inputs = 10..17 |> Enum.to_list # 正确的逐行输出方式 inputs |> Enum.map(fizz_buzz_rem) |> Enum.each(&IO.puts/1)
完整修正后的代码
fizz_buzz = fn (0, 0, _) -> "FizzBuzz" (0, _, _) -> "Fizz" (_, 0, _) -> "Buzz" (_, _, c) -> to_string(c) end fizz_buzz_rem = fn n -> fizz_buzz.(rem(n, 3), rem(n, 5), n) end # 单独调用的正常输出 IO.puts(fizz_buzz_rem.(10)) IO.puts(fizz_buzz_rem.(11)) IO.puts(fizz_buzz_rem.(12)) IO.puts(fizz_buzz_rem.(13)) IO.puts(fizz_buzz_rem.(14)) IO.puts(fizz_buzz_rem.(15)) IO.puts(fizz_buzz_rem.(16)) IO.puts(fizz_buzz_rem.(17)) IO.puts("----------------") inputs = 10..17 |> Enum.to_list # 逐行输出map后的结果 inputs |> Enum.map(fizz_buzz_rem) |> Enum.each(&IO.puts/1) IO.puts("----------------") inputs |> Enum.map(fn n -> fizz_buzz.(rem(n, 3), rem(n, 5), n) end) |> Enum.each(&IO.puts/1) IO.puts("----------------") manual_inputs = [10, 11, 12, 13, 14, 15, 16, 17] manual_inputs |> Enum.map(fizz_buzz_rem) |> Enum.each(&IO.puts/1) IO.puts("----------------") manual_inputs |> Enum.map(fn n -> fizz_buzz.(rem(n, 3), rem(n, 5), n) end) |> Enum.each(&IO.puts/1) IO.puts("----------------") result = [ fizz_buzz_rem.(10), fizz_buzz_rem.(11), fizz_buzz_rem.(12), fizz_buzz_rem.(13), fizz_buzz_rem.(14), fizz_buzz_rem.(15), fizz_buzz_rem.(16), fizz_buzz_rem.(17), ] # 如果要查看列表的完整结构,用IO.inspect IO.inspect(result) # 逐行输出结果列表 Enum.each(result, &IO.puts/1)
运行这段代码后,你就能看到和单独调用完全一致的预期输出了。
内容的提问来源于stack exchange,提问作者Sol
相关产品推荐
相关产品推荐

