ãã®èšäºã¯ äžäŒ.com Advent Calendar 2024 ã®3æ¥ç®ã®èšäºã§ãã æšä»ã¯æã
äžäŒã®ãããªäºçŽã·ã¹ãã éçºã«ãããŠãã颿°åããã°ã©ãã³ã°ç±æ¥ã®ãã©ã¯ãã£ã¹ãåãå
¥ããæ©äŒãå¢ããŠããŸãã äŸãã°ãå€ã¯ã€ãã¥ãŒã¿ãã«ã§ããæ¹ãæ±ããããã颿°ã¯å¯äœçšã®ãªãçŽç²é¢æ°ã«ããæ¹ããã¹ã¿ããªãã£ãªã©ãå«ãäœããšéœåããããããããå Žé¢ã§ã¯ç©æ¥µçã«äžå€ãªå€ã䜿ãã颿°ãåªçã«ãªãããæèçã«å®è£
ããŸãããã¡ã€ã³ããžãã¯ãçŽç²é¢æ°ãšããŠèšè¿°ã§ãããšãå
ç¢ã§è²¬ååé¢ãããããããã¹ãããããã°ãããããã·ã¹ãã ã«ãªã£ãŠãããŸãã ãšããã§ã颿°åããã°ã©ãã³ã°ãšã¯ãªãããããšããã®ã«æç¢ºãªå®çŸ©ã¯ãªãããã§ããã§ããçªãè©°ããŠãããšãèšç®ããªãã¹ããæãã§ã¯ãªããåŒãã§å®£èšããããšãäžã€ã®ç®æšã ãšããããšã«æ°ãã€ããŸãã æãšåŒã®éãã¯äœã§ãããã? for æã代å
¥æãif æãªã©ã®æã¯ãåºæ¬çã«ã¯å€ãè¿ããŸãããå€ãè¿ããªããšããããšã¯ãæã¯çŽæ¥çµæãåãåããã®ã§ã¯ãªããåœä»€ã«ãªã£ãŠãããšèšããŸããæã¯èšç®æ©ãžã®åœä»€ã§ãã äžæ¹ã®åŒã¯ãå¿
ãè¿å€ã䌎ããŸãããããã®äž»ãªç®çã¯è¿å€ãåŸããã€ãŸãåŒãè©äŸ¡ããŠèšç®ã®çµæãåŸãããšã ãšèããããšãã§ããŸãã customer.archive() ãšãæã«ãã£ãŠæé»çã« customer ãªããžã§ã¯ãã®å
éšç¶æ
ã倿Žããã®ã§ã¯ãªã const archivedCustomer = archiveCustomer(customer) ãšãåŒæ°ã§äžãããã customer ãªããžã§ã¯ããçŽæ¥å€æŽããããšãªãã«ãã¢ãŒã«ã€ãç¶æ
ã«å€æŽãããã³ããŒãšããŠã® archivedCustomer ãªããžã§ã¯ããè¿å€ãšããŠè¿ãããããåŒã§ãããã®é¢æ°ã¯çŽç²é¢æ°ãšããŠå®è£
ããcustomer ãªããžã§ã¯ãã¯äžå€ãã€ãŸãã€ãã¥ãŒã¿ãã«ãªãã®ãšããŠæ±ããšè¯ãã§ãããã åŒã«ããã€ãã¥ãŒã¿ãã«ãªãªããžã§ã¯ãã®æŽæ°ã¯ TypeScript ãªã export const archiveCustomer = ( customer : Customer ): Customer => ( { ...customer, archived : true } ) ãšãã¹ãã¬ããæ§æã䜿ãããšã§ customer ãªããžã§ã¯ãã®ã³ããŒãäœãã€ã€ã倿Žãããããããã£ãæ°ããªå€ã«èšå®ãããã®ãè¿ãããã«å®è£
ããŸãã ãã®ããã«ãåŒæ°ã§äžãããªããžã§ã¯ãã¯çŽæ¥å€æŽãããç¶æ
ã倿Žããå¥ã®ãªããžã§ã¯ããè¿ããããªé¢æ°ã®é£ãªãã«ãã£ãŠèšç®ãå®çŸ©ããŠããã®ã颿°åããã°ã©ãã³ã°ã§ãã ãã®ãããã®èãæ¹ã«ã€ããŠã¯ãéå»ã®çºè¡šã¹ã©ã€ãããããŸãã®ã§åèã«ããŠãã ããã å®éãæã
ã®äžéšãããã¯ãã®ããã¯ãšã³ãã§ã¯ TypeScript ã«ãã颿°åã¹ã¿ã€ã«ã§ã®éçºãå®è·µããŠããŸãã以äžã¯ãããã¯ãã®ã³ãŒãã®äžäŸã§ãCustomer ãªããžã§ã¯ãã«æ°ããã¡ãŒã«ã¢ãã¬ã¹ã®å€ã远å ããããã® addEmail 颿°ã§ããå
ã®å®è£
ã«åãããã¹ãã¬ããæ§æã䜿ã£ãŠå
ã®ãªããžã§ã¯ããç Žå£ããã«ãã¡ãŒã«ã¢ãã¬ã¹ã远å ããããªããžã§ã¯ããè¿ããŸãã const addEmail = ( address : EmailAddress ) => ( customer : Customer ): Customer => { const newAddress: CustomerEmail = { id : generateCustomerEmailId(), address , } return { ...customer, emails : [ ...customer.emails, newAddress ] , } } ãã¡ã€ã³ãªããžã§ã¯ãã®ç¶æ
é·ç§»ã¯ãã¹ãŠããã®åŒã«ããç¶æ
é·ç§»ã®ã¢ãã«ã§å®è£
ããŠããŸãã æ°žç¶ããŒã¿ããã°ã©ãã³ã° ããŠãæ¬èšäºã®ããŒãã¯ãæ°žç¶ããŒã¿ãã§ããæ°žç¶ããŒã¿ãšã¯äœã§ãããã? åŒãæèçã«äœ¿ãããã€å€ãã€ãã¥ãŒã¿ãã«ã«æ±ãããšãåºæ¬ãšããŠãã£ãŠãããšãäœæ°ãªãæžããããã°ã©ã ã®äžã«ç¹åŸŽçãªæ§åãçŸããããšã«ãªããŸãã 以äžããªã¹ãæäœã®ããã°ã©ã ãèŠãŠã¿ãŸãããããªã¹ãã®å
é ãæ«å°Ÿã«å€ã远å ããããé©åœãªå€ãåé€ãã TypeScript ã®ããã°ã©ã ã§ãããªã¹ããã€ãã¥ãŒã¿ãã«ã«æ±ãã¹ããå€ã®è¿œå ãåé€ãªã©ããŒã¿æ§é ã®å€æŽã«ã¯ã¹ãã¬ããæ§æã䜿ããéç Žå£çã«ãããè¡ãããã«ããŸãã // as1: å
ã®ãªã¹ã const as1 = [ 1 , 2 , 3 , 4 , 5 ] ; // as2: æ°ãããªã¹ã (å
é ã« 100 ã远å ) const as2 = [ 100 , ...as1 ] ; // as3: æ°ãããªã¹ã (æ«å°Ÿã« 500 ã远å ) const as3 = [ ...as2, 500 ] ; // as4: æ°ãããªã¹ã (å€ 3 ãåé€) const as4 = as3. filter ( x => x !== 3 ); console . log ( "as1:" , as1); // [1, 2, 3, 4, 5] console . log ( "as2:" , as2); // [100, 1, 2, 3, 4, 5] console . log ( "as3:" , as3); // [100, 1, 2, 3, 4, 5, 500] console . log ( "as4:" , as4); // [100, 1, 2, 4, 5, 500] æŽæ°ãããŠãå
ã®ãªã¹ãã¯äžå€ãªã®ã§ã as1 ãåç
§ããŠãæŽæ°æžã¿ã®çµæã¯åŸãããŸããããªã¹ãæäœã®è¿ãå€ã as2 as3 as4 ãšãã®éœåºŠå€æ°ã«ãã£ããã£ãããã®ãã£ããã£ãã倿°ã«å¯ŸããŠæ¬¡ã®ãªã¹ãæäœãè¡ããŸããããããŠããŒã¿æ§é ã¯äžå€ã§ããã€ã€ãäžé£ã®ãé£ç¶ãããªã¹ãæäœã衚çŸããŸãã ããŒã¿æ§é ãäžå€ã«ããçµæããªã¹ããæŽæ°ãããéçšã®ç¶æ
ãã¹ãŠãæ®ããŸããããªã¹ããäœåºŠãæŽæ°ããã«ãé¢ãããã倿Žåã®ç¶æ
ãåç
§ããããšãã§ããŠããŸãã as1 ãåç
§ããã°åæç¶æ
ãã as2 ã as3 ã§éäžã®ç¶æ
ãåç
§ããããšãã§ããŸãããã®ããã«å€ã®å€æŽåŸããã以åã®ç¶æ
ãæ®ãããŸããæ°žç¶ããŒã¿ããšåŒã³ãŸãããããŠæ°žç¶ããŒã¿ãçšããããã°ã©ãã³ã°ããæ°žç¶ããŒã¿ããã°ã©ãã³ã°ããšåŒã³ãŸãã å€ãã€ãã¥ãŒã¿ãã«ã«æ±ããšå¿
ç¶çã«ããã¯æ°žç¶ããŒã¿ã«ãªãã®ã§ãæ°žç¶ããŒã¿ããã°ã©ãã³ã°ã¯ããèªäœãäœãç¹å¥ãªãã¯ããã¯ãšããããã§ã¯ãããŸãããäžæ¹ã§ãå€ãæ°žç¶ããŒã¿ã§ããããšãã¯ã£ããããããæèäžã§ã¯ãæ°žç¶ããŒã¿ããã°ã©ãã³ã°ããšããèšèã§ããã°ã©ãã³ã°ã¹ã¿ã€ã«ã衚çŸãããšããã®æå³ãæç¢ºã«ãªãããšãå€ãã§ãããã 以äžã®å±±æ¬å圊ããã®èšäºã§ã¯ã颿°åããã°ã©ãã³ã°ããªãã¡ãæ°žç¶ããŒã¿ããã°ã©ãã³ã°ãã§ãããæ°žç¶ããŒã¿ãé§äœ¿ããŠåé¡ãè§£ãããšããã颿°åããã°ã©ãã³ã°ã ããšè¿°ã¹ãããŠããŸãã çè
ã®é¢æ°ããã°ã©ãã³ã°ã®å®çŸ©ãããªãã¡ãã®ç¹éã§ã®å®çŸ©ã¯ããâ æ°žç¶ããŒã¿ããã°ã©ãã³ã°ãã§ããæ°žç¶ããŒã¿ãšã¯ãç Žå£ã§ããªãããŒã¿ãã€ãŸãå代å
¥ã§ããªãããŒã¿ã®ããšã§ãããããŠãæ°žç¶ããŒã¿ãé§äœ¿ããŠåé¡ãè§£ãã®ãæ°žç¶ããŒã¿ããã°ã©ãã³ã°ã§ãã ãŸã颿°åèšèªãšã¯ãæ°žç¶ããŒã¿ããã°ã©ãã³ã°ã奚å±ãæ¯æŽããŠããèšèªã®ããšã§ãã颿°åèšèªã§ã¯ãå代å
¥ã®æ©èœããªãããå代å
¥ã®äœ¿çšã¯éå®ãããŠããŸããçè
ã®å®çŸ©ã¯ããªãå³ããã»ãã ãšèšããŸãã 第1ç« ã颿°ããã°ã©ãã³ã°ã¯é£ãããªãïŒâåããŠåŠã¶äººã«ããæ«æãã人ã«ããã¡ããšããã | gihyo.jp åœä»€åããã°ã©ãã³ã°ã«ãããŠã¯å€æŽã«ãããå€ãçŽæ¥ç Žå£çã«å€æŽããŸãã倿Žåã®ããŒã¿æ§é ã®ç¶æ
ãåç
§ããããšã¯ã§ããŸããããªã¹ãã®ç Žå£ç倿Žã¯ãåºæ¬çã« (åŒã§ã¯ãªã) æã«ãã£ãŠè¡ãããã§ããããæãäž»äœãšããããã°ã©ãã³ã°Â·Â·Â· åœä»€åããã°ã©ãã³ã°ã§ã¯ãæ°žç¶ã§ã¯ãªãããŒã¿ãã€ãŸãçåœããŒã¿ãåºæ¬ã«ããŠãããšèšããŸããäžæ¹ãåŒã«ãã£ãŠããã°ã©ã ãæ§æãã颿°åããã°ã©ãã³ã°ã§ã¯ã颿°ã®åªçæ§ã確ä¿ãã¹ãã€ãã¥ãŒã¿ãã«ã«å€ãæ±ãããšã«ãªãã®ã§ãæ°žç¶ããŒã¿ãåºæ¬ã«ãªããŸãã ã€ãã¥ãŒã¿ãã«ãªå€ã«ããããã°ã©ãã³ã°ãããéãããã«ããå€ã¯äžå€ã§ããã ãã§ãªããåæã«æ°žç¶ããŒã¿ãªã®ã ãšããããšãèªèã§ãããšãããã°ã©ãã³ã°ã¹ã¿ã€ã«ã«å¯Ÿããããããã¡ã³ã¿ã«ã¢ãã«ãæ§ç¯ã§ãããšæããŸãã Haskell ãšæ°žç¶ããŒã¿ããã°ã©ãã³ã° ããåçªã§ãããã€ãã¥ãŒã¿ãã«ãšããã°çŽç²é¢æ°åèšèªã® Haskell ã§ããå
ã® TypeScript ã«ãããªã¹ãæäœã®ããã°ã©ã ããHaskell ã§å®è£
ããŠã¿ãŸãã main :: IO () main = do let as1 = [ 1 , 2 , 3 , 4 , 5 ] as2 = 100 : as1 as3 = as2 ++ [ 500 ] as4 = delete 3 as3 print as1 -- [1,2,3,4,5] print as2 -- [100,1,2,3,4,5] print as3 -- [100,1,2,3,4,5,500] print as4 -- [100,1,2,4,5,500] Haskell ã¯ãªã¹ãã¯ãã¡ãããåºæ¬çã«å€ããããããã€ãã¥ãŒã¿ãã«ã§ãããªã¹ãæäœã® API ã¯ãã¹ãŠéç Žå£çã«ãªãããå®è£
ãããŠããã®ã§ã倿Žã«ããã TypeScript ã®ããã«ã¹ãã¬ããæ§æã§ããŒã¿ãæç€ºçã«ã³ããŒãããããå¿
èŠã¯ãããŸãããè£ãè¿ãã°ã倿Žã¯æ°žç¶ããŒã¿çã«è¡šçŸããããåŸããåŒã«ãã£ãŠããã°ã©ã ãæ§æããããšãå¿
é ãšãªããŸããçµæãHaskell ã«ããå®è£
ã¯èªç¶ãšæ°žç¶ããŒã¿ããã°ã©ãã³ã°ã«ãªããŸãã 颿°åããã°ã©ãã³ã°ããªãã¡æ°žç¶ããŒã¿ããã°ã©ãã³ã°ã ããšããã®ã¯ããã®å¿
ç¶æ§ããæ¥ãŠããŸãã æ°žç¶ããŒã¿ã®ç¹æ§ãå©çšããåé¡è§£æ±º æ°žç¶ããŒã¿ããã°ã©ãã³ã°ã¯äžå€ãªå€ã䜿ãããšã§ãããããããå®è·µããããšã§èšäºåé ã§æãããããªããã°ã©ã ã®å
ç¢æ§ãªã©æ§ã
ãªã¡ãªããã享åã§ããããã§ãããã倿Žåã®éå»ã®ç¶æ
ãåç
§ã§ããããšãããå€ãäžå€ã§ãããšããããã¯ããŸãã«ãæ°žç¶ãããŒã¿ã®ç¹æ§ãéšåãæŽ»ããã±ãŒã¹ããããŸãã ãããããã顿ãšããŠãç«¶æããã°ã©ãã³ã°ã®åé¡ãäŸã«æããŸãã atcoder.jp å顿ãèªãã®ãé¢åãªæ¹ã®ããã«ããããã©ããªåé¡ãç°¡åã«è§£èª¬ããŸããå
¥åã®æç€ºã«åŸã£ãŠãªã¹ããæŽæ°ãã€ã€ãä»»æã®ã¿ã€ãã³ã°ã§ãã®ãªã¹ãã®çŸåšã®ç¶æ
ãä¿åããããŸãä»»æã®ã¿ã€ãã³ã°ã§åŸ©å
ã§ãããããããšãããããŒã¿æ§é ã®ä¿åãšåŸ©å
ã顿ã«ããåé¡ã§ãã ADD 3 SAVE 1 ADD 4 SAVE 2 LOAD 1 DELETE DELETE LOAD 2 SAVE 1 LOAD 3 LOAD 1 ããããã¯ãšãªãå
¥åãšããŠäžããããã 空ã®ãªã¹ããæåã«ãã ã¯ãšãªãäžããé çªã«è§£éããŠã ADD 3 ã®ãšãã¯ãªã¹ãæ«å°Ÿã« ã远å ãã DELETE ãªãæ«å°Ÿã®å€ãåé€ SAVE 1 ã®ãšãã¯ãä»äœ¿ã£ãŠãããªã¹ãã ID çªå· ã®é åã«ä¿åã LOAD 1 ãªã ID çªå· ã®é åãããªã¹ãã埩å
ãã ã¯ãšãªã®ãã³ããã®æç¹ã§ã®ãªã¹ãã®æ«å°Ÿã®èŠçŽ ãåºåãã ãšããåé¡ã«ãªã£ãŠããŸãã ãã®åé¡ãæ°žç¶ããŒã¿ãªãã§è§£ãããšãããšããªã¹ããæŽæ°ããŠã以åã®ç¶æ³ã«æ»ãããããªæšã®ããŒã¿æ§é ãèªåã§æ§ç¯ããå¿
èŠããããªããªãé¢åã§ããäžæ¹ãæ°žç¶ããŒã¿ãåæã«ãããšãäœã®èŠåŽããªãè§£ããŠããŸããŸãã 以äžã¯ Haskell ã§å®è£
ããäŸã§ãããã£ãŠããããšã¯ãã¯ãšãªã®å
容ã«åãããŠãªã¹ãã«å€ã远å ã»åé€ãä¿åãšåŸ©å
ã®ãšãã¯èŸæž (IntMap) ã«ããã®æç¹ã®ãªã¹ããæ ŒçŽããŠããã ãã§ããå顿ã®éãã«ã·ãã¥ã¬ãŒã·ã§ã³ããŠããã ãããšãèšããŸãã main :: IO () main = do q <- readLn @ Int qs <- map words <$> replicateM q getLine let qs' = [ if null args then (command, - 1 ) else (command, stringToInt (head args)) | command : args <- qs] let res = scanl' f ([], IM.empty) qs' where f (xs, s) query = case query of ( "ADD" , x) -> (x : xs, s) -- ãªã¹ãã«å€ã远å ( "DELETE" , _) -> (drop1 xs, s) -- ãªã¹ãããå€ãåé€ ( "SAVE" , y) -> (xs, IM.insert y xs s) -- èŸæžã«ãã®æç¹ã®ãªã¹ããä¿å ( "LOAD" , z) -> (IM.findWithDefault [] z s, s) -- èŸæžããä¿åãããªã¹ãã埩å
_ -> error "!?" printList [headDef ( - 1 ) xs | (xs, _) <- tail res] -- åã¯ãšãªã®ã¿ã€ãã³ã°ã§ã®ãªã¹ãã®å
é èŠçŽ ãåŸãŠãåºå Haskell ã®ãªã¹ãã¯æ°žç¶ããŒã¿ã§ããããå€ã倿ŽããŠã倿Žä»¥åã®å€ãæ®ããŸãããã®å€ãæé»çã«ä»ã§æžãæããããäºã¯ãããŸããããã£ãŠçŽ çŽã«ãªã¹ããèŸæžã«ä¿åããŠããã°ããã®ã§ããäžæ¹ãåœä»€åããã°ã©ãã³ã°ã«ãããŠãªã¹ãããã¥ãŒã¿ãã«ãªå Žåã¯ãããæç¹ã®åç
§ãèŸæžã«ä¿åãããšããŠããã©ããã§æžãæããçºçãããšãèŸæžã«ä¿åãããåç
§ã®å
ã®ããŒã¿ãæžãæããããããŸããããŸããã æ°žç¶ããŒã¿æ§é ããŠããããããæ¬é¡ã§ããTypeScript ã§ãªã¹ããæ°žç¶ããŒã¿ãšããŠæ±ãã«ããããã¹ãã¬ããæ§æã«ããã³ããŒã䜿ããŸããã // as2: æ°ãããªã¹ã (å
é ã« 100 ã远å ) const as2 = [ 100 , ...as1 ] ; // as3: æ°ãããªã¹ã (æ«å°Ÿã« 500 ã远å ) const as3 = [ ...as2, 500 ] ; ãã§ã«ãæ°ã¥ãã®æ¹ãå€ããšæããŸãããå€ã®æŽæ°ã«ãããããªã¹ãå
šäœã®ã³ããŒãèµ°ã£ãŠããŸã£ãŠããŸããäžã€å€ã远å ãåé€ãæŽæ°ããã ãã§ããªã¹ãã®èŠçŽ ä»¶ã«å¯Ÿã ä»¶ã®ã³ããŒãèµ°ããã€ãŸã ã®èšç®éãå¿
èŠã«ãªã£ãŠããŸããŸããæ°žç¶ããŒã¿ããã°ã©ãã³ã°ã¯è¯ããã®ã§ããããã€ãŒãã«å®è£
ãããšããŒã¿ã³ããŒã«ããèšç®éã®å¢å€§ãæããã¡ã§ãã Haskell ãªã©ãã€ãã¥ãŒã¿ãã«ãåæã®ããã°ã©ãã³ã°èšèªã¯ãã®åé¡ãã©ãããŠããã®ã§ãããã? çµè«ãããŒã¿æ§é å
šäœãã³ããŒããã®ã§ã¯ãªãã倿ŽãããããŒããšãã®ããŒããžçŽæ¥çã»éæ¥çã«åç
§ãæã€ããŒãã ããã³ããŒãããããšã«ãã£ãŠèšç®éãæããäžå€ã§ãããªãããå¹ççãªããŒã¿æŽæ°ãå¯èœã«ãªãããã«ãªã¹ããã®ä»ã®ããŒã¿æ§é ãå®è£
ãããŠããŸããã€ãŸãåãããªã¹ããã§ããåœä»€åããã°ã©ãã³ã°ã®ãããšãäžå€ãªããŒã¿æ§é ã®ããã¯å®è£
èªäœãç°ãªãã®ã§ããæœè±¡ã¯åãããªã¹ããã§ãå
·äœãéããšèšããã§ãããã 倿Žãã£ããšããã ããã³ããŒãããã以å€ã¯å
ã®å€ãšå
±æãè¡ããã®ããŒã¿æ§é ã®å®è£
ææ³ã¯ Structural Sharing ãšåŒã°ããããšããããŸããStructural Sharing ã«ããäžå€ã§ãããªããå¹ççã«æŽæ°ãå¯èœãªæ°žç¶ããŒã¿ã®ããŒã¿æ§é ããæ°žç¶ããŒã¿æ§é ããšåŒã³ãŸãã æ°žç¶ããŒã¿æ§é ã«ã€ããŠã¯ã以äžã®æžç±ã«ãã®å®è£
æ¹æ³å«ã詳ããèšèŒãããŠããŸãã çŽç²é¢æ°åããŒã¿æ§é - ã¢ã¹ããŒãã¯ã³ãŽ ããšããäŸãã° Haskell ã®ãªã¹ãã¯å
é ã®å€ãæäœããå Žå㯠ã§ããå
é èŠçŽ ã ããã³ããŒãããŠããŠããã以éã®èŠçŽ ãæŽæ°ååŸã®äºã€ã®ãªã¹ãã§å
±æãããããã§ãã åãããå
ã®å®è£
ã§ãå©çšãã Data.IntMap ãšããèŸæžããã¡ããæ°žç¶ããŒã¿æ§é ã§ãããå
éšçã«ã¯ãããªã·ã¢æšã§å®è£
ãããŠããŠãå€ã®æ¿å
¥ãããŒã®æ¢çŽ¢ã¯ãæŽæ°ã®ãããé·çšåºŠã®èšç®é ··· ãããŒã¿ãµã€ãºã ããããé·ãšãããšã ã«åãŸããŸãã Haskell ã§å©çšããæšæºçãªããŒã¿æ§é ··· ListãMapãSetãSequenceãHeap ã¯ããã¹ãŠã€ãã¥ãŒã¿ãã«ã§ãããªãããå€ã®æ¢çŽ¢ã倿Žã ã çšåºŠã®èšç®éã§è¡ããæ°žç¶ããŒã¿æ§é ã«ãªã£ãŠããŸãã(ãªãã誀解ã®ç¡ãããè£è¶³ãããšããã¥ãŒã¿ãã«ãªããŒã¿æ§é ããããŸãããã¥ãŒã¿ãã«ãªããŒã¿æ§é ã¯æç¶ãçããã°ã©ãã³ã°ã§å€æŽããããšã«ãªããŸã) æ°žç¶ããŒã¿æ§é ãå©çšããããšã«ãã£ãŠãæ°žç¶ããŒã¿ããã°ã©ãã³ã°æã«ãããã©ãŒãã³ã¹ãããã»ã©ç ç²ã«ããã倧éã®ããŒã¿ãæ±ãããšãå¯èœã«ãªããŸããè£ãè¿ãã°ãæ°žç¶ããŒã¿ããã°ã©ãã³ã°ãããåºç¯å²ã«å®è·µããŠããã«ã¯ãæ°žç¶ããŒã¿æ§é ãå¿
èŠäžå¯æ¬ ã§ãããšãèšããŸãã颿°åããã°ã©ãã³ã°ã¯å€ãäžå€ã§ããããšããããšããŸããããã®ããã«ã¯æ°žç¶ããŒã¿æ§é ãå¿
èŠãã€éèŠãªããŒããªã®ã§ãã TypeScript ãã®ä»ã®ããã°ã©ãã³ã°èšèªã§æ°žç¶ããŒã¿ããã°ã©ãã³ã°ãå®è·µãããšããçŽç²é¢æ°åèšèªãšã¯ç°ãªããçŽ ã®ç¶æ
ã§ã¯æ°žç¶ããŒã¿æ§é ã®æ¯æŽããªããšããããšã¯å¿µé ã«çœ®ããŠããã¹ãã§ãããã TypeScript ã Python ã§æ°žç¶ããŒã¿æ§é ãå©çšããã«ã¯? TypeScript ã® ArrayãMapãSet ãªã©ã®æšæºçãªããŒã¿æ§é ã¯ãã¹ãŠåœä»€åããŒã¿æ§é ãã€ãŸããã¥ãŒã¿ãã«ã§ããåœä»€åã®ããã°ã©ãã³ã°èšèªã«ãããŠã¯ãã©ã®èšèªãåæ§ã§ããããäžæ¹ãããã°ã©ãã³ã°èšèªã«ãã£ãŠã¯ ListãMapãSet ãªã©ã®æ°žç¶ããŒã¿æ§é ããŒãžã§ã³ãæäŸãããµãŒãããŒãã£ã©ã€ãã©ãªããããŸãã Immutable.js (JavaScript / TypeScript) pyrsistent · PyPI (Python) ãããã®ã©ã€ãã©ãªãå°å
¥ããããšã§ãTypeScript ã Python ã§æ°žç¶ããŒã¿æ§é ãå©çšããããšãã§ããŸããããããå®éã®ãšãããããã®æ°žç¶ããŒã¿æ§é ã®å®è£
ããåºãæ®åããŠããããã«ã¯æããŸããã æ°žç¶ããŒã¿æ§é ã¯æ¥åã·ã¹ãã éçºã«ãå¿
é ã? çµè«ãããããšãåœä»€åã®ããã°ã©ãã³ã°èšèªã§æ¥åã·ã¹ãã éçºãããå Žåã«ã¯ãå¿
é ã§ã¯ãªãã§ãããã æ°žç¶ããŒã¿ããã°ã©ãã³ã°èªäœã¯è¯ãäœæ³ã§ãããæ¥åã·ã¹ãã ã«ãããŠã¯ã倧éããŒã¿ã®ãã€ãŒããªã³ããŒãèµ°ããããªå®è£
ãããå Žé¢ãå°ãªãããããšããã®ãçç±ã ãšæããŸãã Haskell ã®ãããªé¢æ°åèšèªã䜿ã£ãŠããã®ã§ããã°ãæ°žç¶ããŒã¿æ§é ã¯æšæºçã«æäŸãããŠããŠãããããå¿
é ãã©ããããæ°ã«ããå¿
èŠããããŸãããæ°žç¶ããŒã¿æ§é ã®ã¡ã«ããºã ãå
šãç¥ããªããŠããèªç¶ã«ããã䜿ã£ãããã°ã©ã ãæžãããã«å°ãããŸãã åœä»€åèšèªã䜿ãã€ã€ããæ°žç¶ããŒã¿ããã°ã©ãã³ã°ãå®è·µããã±ãŒã¹ã§ã¯ã©ãã§ãããã? é床ãå¿
èŠãªå€ãã®å Žé¢ã§ã¯ããã£ããæ°žç¶ããŒã¿ã諊ããåã«åœä»€åããŒã¿æ§é ãå©çšããã°äºè¶³ããã®ã§ãããããæ°žç¶ããŒã¿æ§é ãæã¡åºãå¿
èŠã¯ãªãã§ãããããã¡ã€ã³ãªããžã§ã¯ãã®å€æŽãã€ãã¥ãŒã¿ãã«ã«è¡šçŸããããã³ããŒããå Žåãããããã 10 ã 20 çšåºŠã®ããããã£ãã³ããŒããçšåºŠã§ãã³ã㌠1åã«ãããæ°äžä»¶ãšãã£ããªãŒããŒã®ã³ããŒãçºçãããããªããšã¯åžã§ãããã ãã£ãŠæ¥åã·ã¹ãã éçºã«ãã㊠Immutable.js ã pyrsistent ã®ãããªãµãŒãããŒãã£ã©ã€ãã©ãªãç©æ¥µçã«äœ¿ãããå Žé¢ã¯ãå
ã«è§£ããç«¶æããã°ã©ãã³ã°åé¡ã®ããã«ãæ°žç¶ããŒã¿æ§é ã®æ°žç¶ã§ããç¹æ§ãã®ãã®ãæ©èœèŠä»¶ãšããŠå¿
èŠã«ãªãã±ãŒã¹ã«éãããã®ã§ã¯ãªãã? ãšæããŸãã Immutable.js ã®éçºãåæ»ããŠããã®ã¯ãããã³ããšã³ãã§æ°žç¶ããŒã¿æ§é ã®éèŠãä¹ããããã§ãããããã®ãããªããŒã¿æ§é èªäœã¯éåžžã«éèŠãªæŠå¿µã§ãå€ãã®ããã°ã©ãã³ã°èšèªã«ååšããŸããæã
ããã³ããšã³ããšã³ãžãã¢ãäŸåãããã©ãŠã¶ã®å
éšã§ããå¹ççãªããŒã¿åŠçã®ããã«å€çšãããŠããã¯ãã§ããããããããã³ããšã³ããšã³ãžãã¢ãã€ãã¥ãŒã¿ãã«ã«æ±ããŠããã®ã¯åŠçé床ã§ã¯ãªãèšèšã®æ¹åã§ããã ãããããImmutable.js ã«ä»£ãã£ãŠ Immer ãéçããã®ã§ãããã Immutable.jsãšImmerãã¡ãããšäœ¿ãåããŠããŸããïŒ äžæ¹ãçŽç²é¢æ°åèšèªã§ç«¶æããã°ã©ãã³ã°ã®ãããªå€§ããªããŒã¿ãæ±ãããã°ã©ãã³ã°ãè¡ãå Žåãæ°žç¶ããŒã¿æ§é ã¯å¿
é ã§ããããŸãæ°žç¶ããŒã¿æ§é ãå©çšããŠããããšãæèããããšã§ããããå®è£
ãå¯èœã«ãªããšæã£ãŠããŸããå人çã«ã¯ãã®ãæ°žç¶ããŒã¿æ§é ã«ãã£ãŠãããè¯ãå®è£
ãå¯èœã«ãªããç¹ãããæ¬è³ªçã ãšæã£ãŠããŸãã å
ã®ç«¶æããã°ã©ãã³ã°ã®å®è£
ãæ¹ããŠã¿ãŠã¿ãŸãã main :: IO () main = do q <- readLn @ Int qs <- map words <$> replicateM q getLine let qs' = [ if null args then (command, - 1 ) else (command, stringToInt (head args)) | command : args <- qs] let res = scanl' f ([], IM.empty) qs' where f (xs, s) query = case query of ( "ADD" , x) -> (x : xs, s) -- ãªã¹ãã«å€ã远å ( "DELETE" , _) -> (drop1 xs, s) -- ãªã¹ãããå€ãåé€ ( "SAVE" , y) -> (xs, IM.insert y xs s) -- èŸæžã«ãã®æç¹ã®ãªã¹ããä¿å ( "LOAD" , z) -> (IM.findWithDefault [] z s, s) -- èŸæžããä¿åãããªã¹ãã埩å
_ -> error "!?" printList [headDef ( - 1 ) xs | (xs, _) <- tail res] -- åã¯ãšãªã®ã¿ã€ãã³ã°ã§ã®ãªã¹ãã®å
é èŠçŽ ãåŸãŠãåºå (â») ãã®ããã°ã©ã ã§ã¯ãã¯ãšãªã®ãã³ã«ããã®æç¹ã§ã®ãªã¹ãã®å€ãåºåããå¿
èŠããããŸããããäžèšã®ããã°ã©ã ã§ã¯ (ã¯ãšãªã®ãã³ã«éœåºŠåºåãåŸãŠããã®ã§ã¯ãªã) ã¯ãšãªãå
šéšåŠçãçµããŠãããæçµçãªåºåãã€ãŸããã¬ãŒã³ããŒã·ã§ã³ãçµã¿ç«ãŠãŠããŸãã(â») ã®å®è£
ã§ãã ããŒã¿æ§é ãåœä»€åããŒã¿æ§é ã®å Žåãããã¯ãããŸãããããæç¹ã®ããŒã¿æ§é ã®ç¶æ
ã¯ãã®æç¹ã«ããåç
§ã§ããªãããããã¬ãŒã³ããŒã·ã§ã³ããã®ã¿ã€ãã³ã°ã§åŸãå¿
èŠããããŸãã äžæ¹ãæ°žç¶ããŒã¿æ§é ã®å Žåãåã
æç¹ã®ããŒã¿æ§é ã®ç¶æ
ãåŸããã§ãåç
§ã§ããŸãããã¡ã¢ãªäžã«ããŒã¿æ§é ãä¿æããŠãããŠã Structural Sharing ã«ãããããè¥å€§åããããšããããŸããããã®ããã°ã©ã ã®ããã«ãäžæ žã«ãªãèšç® ··· ã€ãŸããã¡ã€ã³ããžãã¯ããã¹ãŠåŠçãçµããŠãããæ¹ããŠãã¬ãŒã³ããŒã·ã§ã³ã«å€æããããšãå¯èœã§ãããã¬ãŒã³ããŒã·ã§ã³ã»ãã¡ã€ã³åé¢ã®èгç¹ã«ãããŠãæ°žç¶ããŒã¿æ§é ãéèŠãªåœ¹å²ãæãããŠããŸãããã®èãæ¹ã¯ãå®è£
ã¹ã¿ã€ã«ã«å€§ããªåœ±é¿ãäžããŸãã ãã®ç¹ã«é¢ãã詳现ã¯ãç«¶æããã°ã©ãã³ã°æèã絡ããŠè©±ãå¿
èŠãããé·ããªããããªã®ã§æ¹ããŠå¥ã®èšäºã«ããããšæããŸãã ããŠãæ¥åã·ã¹ãã éçºã«ã¯å¿
é ãšã¯èšããªããšç§èŠã¯è¿°ã¹ãŸããããåœä»€åããã°ã©ãã³ã°èšèªã§ãå€ãäžå€ã«æ±ããšãããã®ãã€ãŒããªã³ããŒãèµ°ãåé¡ãæèã§ããŠãããã©ããã¯éèŠã§ããããå€ãã®é¢æ°åèšèªã«ãããŠã¯ãã®èª²é¡ãæ°žç¶ããŒã¿æ§é ã«ãã£ãŠè§£æ¶ããŠãããšããããšã¯ãç¥ã£ãŠãããŠæã¯ãããŸããã æ°žç¶ããŒã¿æ§é ã®å®è£
äŸ ãæ°žç¶ããŒã¿æ§é ããšãããšåé¢ããäœããããããªãã®ãæãæµ®ãã¹ããããããŸãããããã®å®è£
æ¹æ³ãç¥ã£ãŠãããšããå°ã身è¿ãªãã®ã«æãããããšæããŸããæ°žç¶ããŒã¿æ§é ã®äžã§ãæ¯èŒçå®è£
ãç°¡åãªãæ°žç¶ã¹ã¿ãã¯ãšæ°žç¶é
åã®å®è£
ã玹ä»ããŠçµããã«ããããšæããŸããå®è£
ã®è©³çްã«ã€ããŠã¯è§£èª¬ããŸããããé°å²æ°ã ãã¿ãŠããã£ãŠãäœãç¹å¥ãªããšãããªããŠãæ®éã«å®è£
ã§ãããã ãªããšããé°å²æ°ãæŽãã§ããããããšæããŸãã æ°žç¶ã¹ã¿ã㯠Haskell ã§å®è£
ããæ°žç¶ã¹ã¿ãã¯ã®äžäŸã§ããååž°ããŒã¿åã§ãªã¹ãã®ãããªããŒã¿æ§é ã宣èšããAPI ãšã㊠head tail (++) ãªã©åºæ¬çãªé¢æ°ãå®è£
ããŸãã 代æ°çããŒã¿åã§ãªã³ã¯ãªã¹ãæ§é ã宣èšããå
é èŠçŽ ãžã®åç
§ãè¿ãããã«å®è£
ããŸããå
é èŠçŽ ãåç
§ããããšã ( head ) ã¯ãå
é èŠçŽ ãžã®åç
§ãããããåãåºãå€ãåŸãã ããå
é 以å€ã®èŠçŽ ãåŸããã€ãŸãåè§£ããããšã ( tail ) ã¯æ¬¡ã®èŠçŽ ãžã®åç
§ãè¿ããããã ãã§æ°žç¶ã¹ã¿ãã¯ãå®è£
ã§ããŸãã äºã€ã®ã¹ã¿ãã¯ãçµåãã ( (++) ) ãšãã¯ã©ãããŠã ããã£ãŠããŸããŸããããã®éãåæ¹ã®ãªã¹ããã³ããŒããã®ã§ã¯ãªãå€ããªã¹ãã®äžæ¹ã ããã³ããŒããã®ããã®äžã€ã¯æ°ãããªã¹ãã§å
±æãããããã«å®è£
ããŠããŸãã import Prelude hiding ((++)) data Stack a = Nil | Cons a (Stack a) deriving (Show, Eq) empty :: Stack a empty = Nil isEmpty :: Stack a -> Bool isEmpty Nil = True isEmpty _ = False cons :: a -> Stack a -> Stack a cons = Cons head :: Stack a -> a head Nil = error "EMPTY" head (Cons x _) = x tail :: Stack a -> Stack a tail Nil = error "EMPTY" tail (Cons _ xs) = xs ( ++ ) :: Stack a -> Stack a -> Stack a Nil ++ ys = ys Cons x xs ++ ys = Cons x (xs ++ ys) main :: IO () main = do let s0 :: Stack Int s0 = empty s1 = cons ( 1 :: Int) s0 s2 = cons ( 2 :: Int) s1 s3 = cons ( 3 :: Int) s1 s4 = s1 ++ s3 print s0 print s1 print s2 print s3 print s4 åºåçµæã¯ä»¥äžã§ãã Nil Cons_1_Nil Cons_2_(Cons_1_Nil) Cons_3_(Cons_1_Nil) Cons_1_(Cons_3_(Cons_1_Nil)) æ°žç¶é
å æ°žç¶é
åã¯ãé
åãšãã£ãŠãåœä»€åã®é
åã®ããã«é£ç¶ããé åã玢åŒã§åç
§ã§ããããã«ããã¢ãã«ã§ã¯ãªããå®å
šäºåæšã§è¡šçŸããŸãã å€ã¯èã«æãããŠãã€ã³ããã¯ã¹ã«ããåç
§æã«ã¯æ ¹ããäºåæšã蟿ã£ãŠç®çã®èãç¹å®ããŸãããã®ãããåç
§æã®èšç®é㯠ã§ã¯ãªã ãšãªããŸãã äºåæšã«ããé
åã®è¡šçŸ æŽæ°æã«ã¯ã倿ŽãããããŒããšãã®ããŒããžçŽæ¥çã»éæ¥çã«åç
§ãæã€ããŒãã ããã³ããŒããããšããèãã«åŸããæ ¹ããæŽæ°å¯Ÿè±¡ã®èãŸã§ã蟿ãçµè·¯äžã®ããŒããã³ããŒããçµè·¯ã³ããŒãšããææ³ã䜿ããŸããçµè·¯ãã³ããŒãããšãã£ãŠããæšã®é«ãçšåºŠã§ãããæŽæ°ãçµå± ã«ãªããŸãã çµè·¯ã³ããŒã«ã€ããŠã¯ Path Copying ã«ããæ°žç¶ããŒã¿æ§é - Speaker Deck ã®ã¹ã©ã€ãããããããããšæããŸãã {-# LANGUAGE DeriveFunctor #-} import Prelude hiding (read) data Tree a = Leaf a | Node (Tree a) (Tree a) deriving (Show, Functor) fromList :: [a] -> Tree a fromList [] = error "Cannot build tree from empty list" fromList [x] = Leaf x fromList xs = let mid = length xs `div` 2 in Node (fromList (take mid xs)) (fromList (drop mid xs)) read :: Int -> Tree a -> a read _ (Leaf x) = x read i (Node left right) | i < size left = read i left | otherwise = read (i - size left) right write :: Int -> a -> Tree a -> Tree a write _ v (Leaf _) = Leaf v write i v (Node left right) | i < size left = Node (write i v left) right | otherwise = Node left (write (i - size left) v right) size :: Tree a -> Int size (Leaf _) = 1 size (Node left right) = size left + size right main :: IO () main = do let arr = fromList [ 1 .. 8 :: Int] print arr print $ read 3 arr let arr' = write 3 42 arr print $ read 3 arr' print $ read 3 arr æ°žç¶ã¹ã¿ãã¯ãæ°žç¶é
åã®å®è£
ãç°¡åã§ãã玹ä»ããŸããã äœãç¹æ®ãªææ³ã䜿ããšãããã®ã§ã¯ãªãã¹ã¿ãã¯ãé
åãªã©ã®æœè±¡ãèŠæ±ããæäœãèãããã®æœè±¡ã«é©ããå
·äœçã§å¹ççãªããŒã¿æ§é ãçšæããããšããã®ãæ°žç¶ããŒã¿æ§é ã®å®è£
ã§ãã ãŸãšã æ°žç¶ããŒã¿ããã°ã©ãã³ã°ãšæ°žç¶ããŒã¿æ§é ã«ã€ããŠè§£èª¬ããŸããã äžå€ãªå€ã䜿ããåŒã§ããã°ã©ã ã宣èšãããšæ°žç¶ããŒã¿ããã°ã©ãã³ã°ã«ãªã æ°žç¶ããŒã¿ããã°ã©ãã³ã°ã§ã¯ã倿Žåã®å€ãç Žå£ããªãã倿ŽåŸã倿Žåã®å€ãåç
§ã§ãããšããç¹åŸŽãæã€ 颿°åããã°ã©ãã³ã°ããªãã¡æ°žç¶ããŒã¿ããã°ã©ãã³ã°ã§ããããšãèãããã æ°žç¶ããŒã¿ããã°ã©ãã³ã°ã«ãããããŒã¿ã³ããŒãæå°éã«çãå¹ççãªå€æŽãå¯èœã«ããäžå€ããŒã¿æ§é ããæ°žç¶ããŒã¿æ§é ã æ¥åã·ã¹ãã éçºã«ãããŠãæ°žç¶ããŒã¿æ§é ã¯å¿
é ãšã¯èšããªããããã©ãŒãã³ã¹ãå¿
èŠãªå Žé¢ã§ãæ°žç¶ããŒã¿æ§é ãæã¡åºã以å€ã®è§£æ±ºæ¹æ³ããã 倧éããŒã¿ãæ±ãããšãåºæ¬ã§ããã€å€ãäžå€ã«æ±ããããªãæ°žç¶ããŒã¿æ§é ã¯å¿
é äžè¬ã®ã·ã¹ãã éçºã«ãããŠãæ©èœèŠä»¶ãšããŠãæ°žç¶ãããŒã¿ãå¿
èŠã«ãªããªããImmutable.js ãšããå©çšããŠãè¯ããã 颿°åããã°ã©ãã³ã°ããäžå€ã§ãããªãããå€ã®å€æŽãã©ã®ããã«å®çŸããŠãããã¯æ°žç¶ããŒã¿æ§é ã«çç®ãããšããçè§£ã§ãã ãšããã話ã§ããã éäžå°ãè§Šãããæ°žç¶ããŒã¿æ§é ãåæã«ããèšç®ã®åé¢ã«ã€ããŠã¯å¥éãããããŠèšäºã«ããããšæããŸãã è¿œèš ä»¥äžã«èšäºã«ããŸããã zenn.dev