Golfscript - 12 ตัวอักษร
{,1\{)*}/}:f
เริ่มต้นใช้งาน Golfscript - Factorial ทีละขั้นตอน
นี่คือบางสิ่งบางอย่างสำหรับผู้ที่พยายามเรียนรู้ Golfscript สิ่งที่จำเป็นต้องมีคือความเข้าใจพื้นฐานของ Golfscript และความสามารถในการอ่านเอกสาร Golfscript
ดังนั้นเราจึงต้องการที่จะลองเครื่องมือใหม่ของเราgolfscript มันเป็นการดีเสมอที่จะเริ่มต้นด้วยสิ่งที่ง่ายดังนั้นเราจึงเริ่มต้นด้วยปัจจัย นี่เป็นความพยายามครั้งแรกโดยใช้พื้นฐานของ pseudocode ที่เรียบง่าย:
# pseudocode: f(n){c=1;while(n>1){c*=n;n--};return c}
{:n;1:c;{n 1>}{n c*:c;n 1-:n;}while c}:f
ช่องว่างถูกนำมาใช้บ่อยมากใน golfscript เคล็ดลับที่ง่ายที่สุดในการกำจัดช่องว่างคือการใช้ชื่อตัวแปรที่แตกต่างกัน โทเค็นทุกตัวสามารถใช้เป็นตัวแปรได้ (ดูหน้าไวยากรณ์ ) ราชสกุลที่มีประโยชน์ในการใช้เป็นตัวแปรที่มีตัวอักษรพิเศษเช่น|
, &
, ?
- โดยทั่วไปสิ่งที่ไม่ใช้ที่อื่นในรหัส สิ่งเหล่านี้ถูกวิเคราะห์คำเป็นโทเค็นอักขระเดียวเสมอ ในทางตรงกันข้ามตัวแปรเช่นn
จะต้องมีพื้นที่เพื่อผลักดันตัวเลขไปยังสแต็คหลังจาก ตัวเลขเป็นตัวแปรที่กำหนดค่าเริ่มต้นเป็นหลัก
และเช่นเคยจะมีแถลงการณ์ที่เราสามารถเปลี่ยนแปลงได้โดยไม่กระทบต่อผลลัพธ์สุดท้าย ใน golfscript ทุกอย่างตรวจสอบการจริงยกเว้น0
, []
, ""
และ{}
(ดูนี้ ) ที่นี่เราสามารถเปลี่ยนเงื่อนไขการออกจากลูปเป็นเพียง{n}
(เราวนรอบเวลาเพิ่มเติมและสิ้นสุดเมื่อ n = 0)
เช่นเดียวกับการเล่นกอล์ฟในภาษาใด ๆ มันช่วยให้ทราบถึงฟังก์ชั่นที่มีอยู่ โชคดีที่รายการนี้สั้นมากสำหรับนักกอล์ฟ เราสามารถเปลี่ยน1-
ไป(
เพื่อบันทึกตัวอักษรอีก ในปัจจุบันรหัสมีลักษณะดังนี้: (เราสามารถใช้1
แทนที่|
นี่ถ้าเราต้องการซึ่งจะปล่อยการเริ่มต้น)
{:n;1:|;{n}{n|*:|;n(:n;}while|}:f
สิ่งสำคัญคือต้องใช้สแต็คอย่างดีเพื่อให้ได้วิธีแก้ปัญหาที่สั้นที่สุด (ฝึกฝึกหัด) โดยทั่วไปหากค่าถูกใช้เฉพาะในส่วนของรหัสขนาดเล็กอาจไม่จำเป็นต้องเก็บค่าไว้ในตัวแปร โดยการลบตัวแปรผลิตภัณฑ์ที่กำลังทำงานอยู่และเพียงแค่ใช้สแต็คเราสามารถบันทึกอักขระได้ค่อนข้างมาก
{:n;1{n}{n*n(:n;}while}:f
นี่คือสิ่งอื่นที่คิด เรากำลังลบตัวแปรn
ออกจากสแต็คที่ส่วนท้ายของตัวลูป แต่จากนั้นกดทันทีหลังจากนั้น ในความเป็นจริงก่อนที่ลูปจะเริ่มต้นเราก็ลบมันออกจากสแต็ก เราควรทิ้งไว้บนสแต็กแทนและเราสามารถทำให้สภาพลูปว่าง
{1\:n{}{n*n(:n}while}:f
บางทีเราสามารถกำจัดตัวแปรได้อย่างสมบูรณ์ ในการทำเช่นนี้เราจะต้องเก็บตัวแปรไว้ในสแต็กตลอดเวลา ซึ่งหมายความว่าเราต้องการสำเนาสองชุดของตัวแปรในสแต็กเมื่อสิ้นสุดการตรวจสอบสภาพดังนั้นเราจะไม่สูญเสียมันหลังจากการตรวจสอบ ซึ่งหมายความว่าเราจะมีข้อมูลซ้ำซ้อน0
ในสแต็คหลังจากที่ลูปสิ้นสุดลง แต่นั่นก็แก้ไขได้ง่าย
นี่นำเราไปสู่while
โซลูชันลูปที่ดีที่สุดของเรา!
{1\{.}{.@*\(}while;}:f
ตอนนี้เรายังต้องการทำให้สิ่งนี้สั้นลง while
เป้าหมายที่ชัดเจนควรจะเป็นคำว่า มองไปที่เอกสารมีสองทางเลือกที่ทำงานได้ - แฉและทำ เมื่อคุณมีทางเลือกต่าง ๆ ให้ลองและชั่งน้ำหนักประโยชน์ของทั้งคู่ แฉคือ 'สวยมากห่วงขณะที่' เพื่อให้เป็นประมาณการเราจะลดลง 5 ตัวอักษรwhile
4 /
เข้า ส่วนdo
เราตัดwhile
3 ตัวอักษรและรวมสองบล็อกเข้าด้วยกันซึ่งอาจช่วยตัวละครอีกหนึ่งหรือสองตัว
จริงๆแล้วมีข้อเสียเปรียบอย่างมากสำหรับการใช้do
ลูป เนื่องจากการตรวจสอบสภาพเสร็จสิ้นหลังจากที่ร่างกายถูกดำเนินการเพียงครั้งเดียวค่าของ0
จะไม่ถูกต้องดังนั้นเราอาจจำเป็นต้องมีคำสั่ง if ฉันจะบอกคุณตอนนี้ว่าการตีแผ่จะสั้นกว่า (การแก้ปัญหาบางอย่างdo
จะมีให้ในตอนท้าย) ไปข้างหน้าและลองใช้รหัสที่เรามีอยู่แล้วต้องการการเปลี่ยนแปลงเล็กน้อย
{1\{}{.@*\(}/;}:f
ที่ดี! ทางออกของเราสั้นมากและเราทำเสร็จแล้วใช่ไหม Nope นี่คือ 17 ตัวอักษรและ J มี 12 ตัวอักษร อย่ายอมรับความพ่ายแพ้!
ตอนนี้คุณกำลังคิดกับ ... การเรียกซ้ำ
การใช้การเรียกซ้ำหมายถึงเราต้องใช้โครงสร้างการแยกสาขา โชคไม่ดี แต่เนื่องจากแฟคทอเรียลสามารถแสดงซ้ำได้อย่างกระชับซ้ำซากนี่เป็นทางเลือกที่เหมาะสมสำหรับการทำซ้ำ
# pseudocode: f(n){return n==0?n*f(n-1):1}
{:n{n.(f*}1if}:f # taking advantage of the tokeniser
นั่นเป็นเรื่องง่าย - ถ้าเราลองเรียกใช้ซ้ำก่อนหน้านี้เราอาจไม่ได้ใช้while
ลูป! ถึงกระนั้นเรามีเพียง 16 ตัวอักษร
อาร์เรย์
โดยทั่วไปอาร์เรย์จะถูกสร้างขึ้นในสองวิธี - การใช้[
และ]
ตัวละครหรือด้วย,
ฟังก์ชั่น หากดำเนินการด้วยจำนวนเต็มที่ด้านบนของสแต็ก,
ส่งคืนอาร์เรย์ของความยาวนั้นด้วย arr [i] = i
สำหรับการวนซ้ำอาร์เรย์เรามีสามตัวเลือก:
{block}/
: push, block, push, block, ...
{block}%
: [push, block, push, block, ... ] (มีความแตกต่างเล็กน้อยเช่นค่ากลางจะถูกลบออกจากสแต็กก่อนการกดแต่ละครั้ง)
{block}*
: push, push, block, push, block, ...
เอกสาร golfscript มีตัวอย่างของการใช้{+}*
เพื่อรวมเนื้อหาของอาร์เรย์ สิ่งนี้ชี้ให้เห็นว่าเราสามารถใช้{*}*
รับผลิตภัณฑ์ของอาร์เรย์ได้
{,{*}*}:f
น่าเสียดายที่มันไม่ง่ายอย่างนั้น องค์ประกอบทั้งหมดถูกปิดโดยหนึ่ง ( [0 1 2]
แทน[1 2 3]
) เราสามารถใช้{)}%
เพื่อแก้ไขปัญหานี้
{,{)}%{*}*}:f
ไม่ค่อยดี สิ่งนี้ไม่จัดการศูนย์อย่างถูกต้อง เราสามารถคำนวณ (n + 1)! / (n + 1) เพื่อแก้ไขสิ่งนี้แม้ว่าค่าใช้จ่ายจะมากเกินไป
{).,{)}%{*}*\/}:f
เราสามารถลองจัดการ n = 0 ในที่เก็บข้อมูลเดียวกันกับ n = 1 นี่เป็นเรื่องที่สั้นมากที่ต้องทำลองและพยายามย่อให้สั้นที่สุดเท่าที่จะทำได้
ไม่ดีดังนั้นจะเรียงลำดับที่ 7 [1\]$1=
ตัวอักษร: โปรดทราบว่าเทคนิคการเรียงลำดับนี้มีจุดประสงค์ที่เป็นประโยชน์เช่นการกำหนดขอบเขตของตัวเลข (เช่น `[0 \ 100] $ 1 =)
นี่คือผู้ชนะที่มีเพียง 3 ตัวอักษร:.! +
ถ้าเราต้องการมีการเพิ่มและการคูณในบล็อกเดียวกันเราควรวนซ้ำทุกองค์ประกอบในอาร์เรย์ เนื่องจากเราไม่ได้สร้างอาร์เรย์หมายความว่าเราควรจะใช้{)*}/
ซึ่งนำเราไปสู่การดำเนินการที่สั้นที่สุดของนักกอล์ฟ! ที่ความยาว 12 ตัวอักษรนี้จะเชื่อมโยงกับ J!
{,1\{)*}/}:f
โซลูชั่นโบนัส
เริ่มต้นด้วยif
วิธีแก้ปัญหาที่ตรงไปตรงมาสำหรับการdo
วนซ้ำ:
{.{1\{.@*\(.}do;}{)}if}:f
เราสามารถบีบสิ่งนี้ออกมาได้สองสามอย่าง ซับซ้อนเล็กน้อยดังนั้นคุณจะต้องโน้มน้าวตัวเองให้ทำงานเหล่านี้ ตรวจสอบให้แน่ใจว่าคุณเข้าใจสิ่งเหล่านี้ทั้งหมด
{1\.!!{{.@*\(.}do}*+}:f
{.!{1\{.@*\(.}do}or+}:f
{.{1\{.@*\(.}do}1if+}:f
ทางเลือกที่ดีกว่าคือการคำนวณ (n + 1)! / (n + 1) ซึ่งไม่จำเป็นต้องใช้if
โครงสร้าง
{).1\{.@*\(.}do;\/}:f
แต่do
ทางออกที่สั้นที่สุดที่นี่ใช้ตัวละครสองสามตัวเพื่อแมป 0 ถึง 1 และทุกอย่างอื่นเพื่อตัวของมันเอง - ดังนั้นเราจึงไม่จำเป็นต้องแยกสาขา การเพิ่มประสิทธิภาพประเภทนี้พลาดได้ง่ายมาก
{.!+1\{.@*\(.}do;}:f
สำหรับทุกคนที่สนใจโซลูชันแบบเรียกซ้ำทางเลือกสองสามทางที่มีความยาวเท่ากันกับด้านบนมีให้ที่นี่:
{.!{.)f*0}or+}:f
{.{.)f*0}1if+}:f
{.{.(f*}{)}if}:f
* หมายเหตุ: ฉันยังไม่ได้ทดสอบโค้ดหลายชิ้นในโพสต์นี้ดังนั้นโปรดแจ้งให้ทราบหากมีข้อผิดพลาด