Elixir - มาโคร

มาโครเป็นหนึ่งในคุณสมบัติขั้นสูงและทรงพลังที่สุดของ Elixir เช่นเดียวกับคุณสมบัติขั้นสูงของภาษาใด ๆ ควรใช้มาโครให้เพียงพอ ทำให้สามารถทำการแปลงรหัสที่มีประสิทธิภาพได้ในเวลารวบรวม ตอนนี้เราจะเข้าใจว่ามาโครคืออะไรและใช้งานอย่างไรโดยสังเขป

ใบเสนอราคา

ก่อนที่เราจะเริ่มพูดถึงมาโครเรามาดู Elixir ภายในกันก่อน โปรแกรม Elixir สามารถแสดงด้วยโครงสร้างข้อมูลของมันเอง ส่วนประกอบพื้นฐานของโปรแกรม Elixir คือทูเพิลที่มีสามองค์ประกอบ ตัวอย่างเช่นผลรวมการเรียกฟังก์ชัน (1, 2, 3) จะแสดงภายในเป็น -

{:sum, [], [1, 2, 3]}

องค์ประกอบแรกคือชื่อฟังก์ชันส่วนที่สองคือรายการคำหลักที่มีข้อมูลเมตาและรายการที่สามคือรายการอาร์กิวเมนต์ คุณจะได้รับสิ่งนี้เป็นผลลัพธ์ในเชลล์ iex หากคุณเขียนสิ่งต่อไปนี้ -

quote do: sum(1, 2, 3)

โอเปอเรเตอร์จะแสดงเป็นทูเพิลเช่นกัน ตัวแปรจะแสดงโดยใช้ triplets เช่นกันยกเว้นว่าองค์ประกอบสุดท้ายคืออะตอมแทนที่จะเป็นรายการ เมื่ออ้างถึงนิพจน์ที่ซับซ้อนมากขึ้นเราจะเห็นว่าโค้ดนั้นแสดงเป็นทูเปิลซึ่งมักจะซ้อนกันภายในโครงสร้างที่คล้ายกับต้นไม้ หลายภาษาจะเรียกการแสดงเช่นนี้ว่าAbstract Syntax Tree (AST). Elixir เรียกสำนวนที่ยกมาเหล่านี้

ไม่ต้องพูด

ตอนนี้เราสามารถดึงโครงสร้างภายในของโค้ดของเราได้แล้วเราจะแก้ไขได้อย่างไร? ในการฉีดรหัสหรือค่าใหม่เราใช้unquote. เมื่อเรายกเลิกการอ้างถึงนิพจน์จะถูกประเมินและฉีดเข้าไปใน AST ให้เราพิจารณาตัวอย่าง (ใน iex shell) เพื่อทำความเข้าใจแนวคิด -

num = 25

quote do: sum(15, num)

quote do: sum(15, unquote(num))

เมื่อรันโปรแกรมข้างต้นจะให้ผลลัพธ์ดังนี้ -

{:sum, [], [15, {:num, [], Elixir}]}
{:sum, [], [15, 25]}

ในตัวอย่างสำหรับนิพจน์ใบเสนอราคาไม่ได้แทนที่ num ด้วย 25 โดยอัตโนมัติเราจำเป็นต้องยกเลิกการอ้างถึงตัวแปรนี้หากเราต้องการแก้ไข AST

มาโคร

ตอนนี้เราคุ้นเคยกับ quote และ unquote แล้วเราสามารถสำรวจ metaprogramming ใน Elixir โดยใช้มาโครได้

ในคำที่ง่ายที่สุดคือฟังก์ชันพิเศษที่ออกแบบมาเพื่อส่งคืนนิพจน์ที่ยกมาซึ่งจะถูกแทรกลงในโค้ดแอปพลิเคชันของเรา ลองนึกภาพมาโครที่ถูกแทนที่ด้วยนิพจน์ที่ยกมาแทนที่จะเรียกว่าเหมือนฟังก์ชัน ด้วยมาโครเรามีทุกสิ่งที่จำเป็นในการขยาย Elixir และเพิ่มโค้ดลงในแอปพลิเคชันของเราแบบไดนามิก

ให้เรานำไปใช้เว้นแต่เป็นมาโคร เราจะเริ่มต้นด้วยการกำหนดมาโครโดยใช้defmacroมาโคร โปรดจำไว้ว่ามาโครของเราจำเป็นต้องส่งคืนนิพจน์ที่ยกมา

defmodule OurMacro do
   defmacro unless(expr, do: block) do
      quote do
         if !unquote(expr), do: unquote(block)
      end
   end
end

require OurMacro

OurMacro.unless true, do: IO.puts "True Expression"

OurMacro.unless false, do: IO.puts "False expression"

เมื่อรันโปรแกรมข้างต้นจะให้ผลลัพธ์ดังนี้ -

False expression

สิ่งที่เกิดขึ้นที่นี่คือโค้ดของเราถูกแทนที่ด้วยโค้ดที่ยกมาที่ส่งคืนโดยมาโครเว้นแต่ เราได้ยกเลิกการอ้างสิทธิ์ของนิพจน์เพื่อประเมินในบริบทปัจจุบันและยังไม่ได้ใส่เครื่องหมายคำสั่ง do block เพื่อดำเนินการในบริบทนั้น ตัวอย่างนี้แสดงให้เราเห็นการเขียนโปรแกรม metaprogramming โดยใช้มาโครใน elixir

มาโครสามารถใช้ในงานที่ซับซ้อนกว่านี้ได้มาก แต่ควรใช้อย่าง จำกัด เนื่องจากโดยทั่วไปแล้ว metaprogramming ถือเป็นแนวทางปฏิบัติที่ไม่ดีและควรใช้เมื่อจำเป็นเท่านั้น