Làm thế nào để tham gia chuỗi trong Elixir?


158

Làm cách nào để tôi nối hai chuỗi trong danh sách với khoảng trắng, như:

["StringA", "StringB"]

trở thành

"StringA StringB"

Câu trả lời:


220

Nếu bạn chỉ muốn tham gia một số danh sách tùy ý:

"StringA" <> " " <> "StringB"

hoặc chỉ sử dụng phép nội suy chuỗi:

 "#{a} #{b}"

Nếu kích thước danh sách của bạn là tùy ý:

Enum.join(["StringA", "StringB"], " ")

... tất cả các giải pháp trên sẽ trở lại

"StringA StringB"

36
Cú pháp thay thế bằng cách sử dụng toán tử đường ống: ["StringA", "StringB"] |> Enum.join " "
Ryan Cromwell

11
Bạn nên tránh người vận hành đường ống khi bạn không thực sự có nhu cầu vận hành đường ống.
Carlos

3
@EdMelo Chăm sóc để giải thích tại sao? Về mặt kỹ thuật, bạn không bao giờ thực sự "cần" các hoạt động đường ống, vì hành vi tương tự có thể đạt được bằng cách gọi các hàm chức năng lồng nhau.
Schrockwell

8
@Schrockwell yeah, "nên" là quá nhiều. Ý tôi là trường hợp này bạn không có khả năng đọc nên một cuộc gọi chức năng đơn giản sẽ khiến suy nghĩ rõ ràng hơn.
Carlos

3
Bạn nên sử dụng càng nhiều ngôn ngữ Elixir càng tốt để chứng minh cho các nhà tuyển dụng tiềm năng mà bạn biết nó. Vì vậy, tôi sẽ sử dụng tất cả các giải pháp trên trong cùng một tệp.
Rodmclaughlin

61

Nếu những gì bạn có là một danh sách tùy ý, thì bạn có thể sử dụng Enum.join, nhưng nếu nó chỉ dành cho hai hoặc ba, thì việc nối chuỗi rõ ràng sẽ dễ đọc hơn

"StringA" <> " " <> "StringB"

Tuy nhiên, thường thì bạn không cần phải có một chuỗi trong bộ nhớ nếu bạn định xuất nó qua mạng. Trong trường hợp đó, có thể thuận lợi khi sử dụng iolist (một loại cụ thể của danh sách sâu), giúp bạn tiết kiệm từ việc sao chép dữ liệu. Ví dụ,

iex(1)> IO.puts(["StringA", " ", "StringB"])
StringA StringB
:ok

Vì bạn sẽ có các chuỗi đó dưới dạng các biến ở đâu đó, bằng cách sử dụng một danh sách sâu, bạn tránh phân bổ một chuỗi hoàn toàn mới chỉ để xuất nó ở nơi khác. Nhiều chức năng trong elixir / erlang hiểu iolists, vì vậy bạn thường không cần phải làm thêm.


Nếu bạn cần thêm một cái gì đó vào cuối lệnh ống "Chuỗi" |> (& (& 1 <> "\ n")). ()
hwatkins

9

Trả lời cho đầy đủ, bạn cũng có thể sử dụng phép nội suy Chuỗi :

iex(1)> [a, b] = ["StringA", "StringB"]
iex(2)> "#{a} #{b}"
"StringA StringB"

5

Nếu bạn ổn với việc thêm một khoảng trắng trong danh sách của mình, bạn có thể coi nó là một người chơi iolist:

["StringA", " ", "StringB"] |> IO.iodata_to_binary # "StringA StringB"

Điều này mang lại cho bạn một số hiệu suất tăng khi bạn không sao chép bất kỳ chuỗi nào trong bộ nhớ.


4

Một Enum.reduce sẽ làm việc quá cho ví dụ của bạn không?

iex(4)> Enum.reduce(["StringA", "StringB"], fn(x, acc) -> x <> " " <> acc end) "StringB StringA"


Có, nhưng nó cần Enum.reduce ngược (["a", "b", "c"] |> Enum.reverse, fn (x, acc) -> x <> "" <> acc end) "ab c "
Andrei Sura

cá nhân nghĩ rằng đây là câu trả lời tốt nhất vì nó khái quát cho các trường hợp khác có thể sử dụng giảm. Nói đến ý tưởng "do.call" trong R.
Thomas Browne

3

Nó phụ thuộc vào những gì bạn đang cố gắng làm. Nếu bạn chỉ đang cố gắng viết ra một biến mới, thì chỉ cần sử dụng một trong hai:

  • Nội suy chuỗi

    a = "StringA"
    b = "StringB"
    "#{a} #{b}"
    
  • Chuỗi kết nối: "StringA" <> " " <> "StringB

  • Enum.join(): ["StringA", "StringB"] |> Enum.join(" ")

Tuy nhiên, như Uri đã đề cập, IOLists cũng có thể được sử dụng:

["StringA", " ", "StringB"] |> IO.iodata_to_binary

IOLists thực sự sẽ là người thực hiện nhiều nhất nếu bạn cần quan tâm đến việc tiêu thụ tài nguyên. Big Nerd Ranch có một bài viết tốt về hiệu suất đạt được w / IOLists.


2

Có một số phương thức, nhưng biết cách nó xử lý các giá trị nil có thể xác định phương thức nào bạn nên chọn.

Điều này sẽ gây ra lỗi

iex(4)> "my name is " <> "adam"
"my name is adam"

iex(1)> "my name is " <> nil
** (ArgumentError) expected binary argument in <> operator but got: nil
    (elixir) lib/kernel.ex:1767: Kernel.wrap_concatenation/3
    (elixir) lib/kernel.ex:1758: Kernel.extract_concatenations/2
    (elixir) lib/kernel.ex:1754: Kernel.extract_concatenations/2
    (elixir) expanding macro: Kernel.<>/2
    iex:1: (file)

Điều này sẽ chỉ chèn một chuỗi "" trống:

iex(1)> "my name is #{nil}"
"my name is "

Như thế này:

iex(3)> Enum.join(["my name is", nil], " ")
"my name is "

Cũng xem xét các loại. Với <>bạn không nhận được bất kỳ casting miễn phí:

iex(5)> "my name is " <> 1
** (ArgumentError) expected binary argument in <> operator but got: 1
    (elixir) lib/kernel.ex:1767: Kernel.wrap_concatenation/3
    (elixir) lib/kernel.ex:1758: Kernel.extract_concatenations/2
    (elixir) lib/kernel.ex:1754: Kernel.extract_concatenations/2
    (elixir) expanding macro: Kernel.<>/2
    iex:5: (file)

iex(5)> "my name is #{1}"
"my name is 1"

iex(7)> Enum.join(["my name is", 1], " ")
"my name is 1"

Hiệu suất trong thực tế có vẻ gần giống nhau:

iex(22)> :timer.tc(fn -> Enum.each(1..10_000_000, fn _ -> "my name is " <> "adam" end) end)
{8023855, :ok}
iex(23)> :timer.tc(fn -> Enum.each(1..10_000_000, fn _ -> "my name is " <> "adam" end) end)
{8528052, :ok}
iex(24)> :timer.tc(fn -> Enum.each(1..10_000_000, fn _ -> "my name is " <> "adam" end) end)
{7778532, :ok}
iex(25)> :timer.tc(fn -> Enum.each(1..10_000_000, fn _ -> "my name is #{"adam"}" end) end)
{7620582, :ok}
iex(26)> :timer.tc(fn -> Enum.each(1..10_000_000, fn _ -> "my name is #{"adam"}" end) end)
{7782710, :ok}
iex(27)> :timer.tc(fn -> Enum.each(1..10_000_000, fn _ -> "my name is #{"adam"}" end) end)
{7743727, :ok}

Vì vậy, thực sự phụ thuộc vào việc bạn có muốn sụp đổ hay không khi các giá trị nội suy là nilhoặc loại sai.


0

Bạn cũng có thể làm 'string A' ++ ' ' ++ 'string B'


7
Họ sẽ không trở thành char-list chứ?
Ảo

0

Cân nhắc sử dụng Danh sách IO, nếu bạn có ["String1", "string2"] và bạn sử dụng iolist_to_binary / 1 trên đó thì bạn sẽ sao chép các chuỗi đó thành một chuỗi mới. Nếu bạn có một danh sách IO, bạn có thể chỉ cần xuất nó trong hầu hết các trường hợp và nó sẽ nối nó trên cổng. Và đây là điều quan trọng, thời gian chạy sẽ không cần tạo các bản sao của dữ liệu để nó hiệu quả hơn nhiều so với ghép nối.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.