challenge LL Golf Hole 3 - 13日の金曜日を数え上げる

今日から2013年12月31日までの、13日の金曜日とその総数を表示してください。

余力のあるものはこのプログラムを短くしてみたり、短くしてみたり、短くしてください。

※LL Future実行委員の高野光弘です。この出題は LL Future公式の出題であり、優れたものについてはLL Golfのセッションでご紹介させていただくかもしれません。ご理解の上、ご投稿ください。また、LL Futureのチケットは現在も発売中です。よろしければ、メインイベントの方にもぜひご参加ください。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
#!/usr/bin/env ruby
require 'date'

from = DateTime.now
to = DateTime.parse("2013-12-31")

friday = (from..to).inject(0) do |friday, date|
    if date.mday == 13 and date.wday == 5 then
        puts date.strftime('%Y-%m-%d')
        friday + 1
    else
        friday
    end
end

puts friday

Posted feedbacks - Nested

Flatten Hidden

毎月の 13 日が金曜かどうかを調べ上げています。

初めに現在が 13 日であるかどうか調べ、 ・13日より前なら、現在の日時を 13 日に設定 ・13日より後なら、現在の日時を翌月の 13 日に設定 とし、調べ上げを開始しています。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import java.util.*;
import static java.util.Calendar.*;
public class Count13Friday {

    public static void main(String[] args) {
        Calendar to = new GregorianCalendar(2013, 12, 31);
        Calendar current = new GregorianCalendar();

        int currentDate = current.get(DATE);
        if (currentDate != 13) {
            current.set(DATE, 13);
            if (13 < currentDate) {
                current.add(MONTH, 1);
            }
        }

        List<Date> fridays = new ArrayList<Date>();
        while (current.compareTo(to) <= 0) {
            if (current.get(DAY_OF_WEEK) == FRIDAY) {
                fridays.add(current.getTime());
            }
            current.add(MONTH, 1);
        }
        System.out.println("Fridays = " + fridays);
    }

}

数の出力が抜けてました。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
bash.exe -c "diff -u old/Count13Friday.java Count13Friday.java"
--- old/Count13Friday.java      2008-08-06 18:51:31.343750000 +0900
+++ Count13Friday.java  2008-08-06 18:49:00.625000000 +0900
@@ -22,6 +22,7 @@
             current.add(MONTH, 1);
         }
         System.out.println("Fridays = " + fridays);
+        System.out.println("Fridays count = " + fridays.size());
     }

 }
JavaはGolf向きじゃないんですよねー,といいつつChallenge。
基本アルゴリズムは同じで,削れるだけ削ってみました。
現在243バイト。
1
import java.util.*;class F{public static void main(String[]a){int n=0,d;for(Calendar c=new GregorianCalendar();c.get(1)<2014;c.add(2,1)){d=c.get(5);c.set(5,13);if(d<=13&&c.get(7)==6){System.out.println(c.getTime());n++;}}System.out.print(n);}}

Squeak Smalltalk で。

1
2
3
4
5
| fridayThe13ths |
fridayThe13ths := (Date today to: 2013 asYear end) dates 
    select: [:date | date dayOfMonth = 13 and: [date dayOfWeekName = #Friday]].
World findATranscript: nil.
^fridayThe13ths do: [:each | Transcript cr; show: each]; size

素直な枝刈りとして毎月の13日だけを調べています。

;; Eager comprehension の終点が exclusive なのでハマったのは秘密。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
(use srfi-1)
(use srfi-19)
(use srfi-42)

(let ((ds (filter (compose (cut = <> 5) date-week-day)
                  (drop (list-ec (: y 2008 2014) (: m 1 13)
                                 (make-date 0 0 0 0 13 m y 0))
                        7))))
  (for-each (compose print (cut date->string <> "~1")) ds)
  (print (length ds)))
SERIESとMETATILITIESを利用してみました。

;>>> Friday, February 13, 2009
;>>> Friday, March 13, 2009
;>>> Friday, November 13, 2009
;>>> Friday, August 13, 2010
;>>> Friday, May 13, 2011
;>>> Friday, January 13, 2012
;>>> Friday, April 13, 2012
;>>> Friday, July 13, 2012
;>>> Friday, September 13, 2013
;>>> Friday, December 13, 2013
;==> 10
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
(use-package :series)

(defun friday13-p (ut)
  (and (= 13 (nth-value 3 (decode-universal-time ut)))
       (= 4 (nth-value 6 (decode-universal-time ut)))))

(let* ((from (get-universal-time))
       (to (encode-universal-time 59 59 23 31 12 2013))
       (uts (choose-if #'friday13-p (scan-range :from from :upto to :by (* 60 60 24)))))
  (iterate ((ut uts)) 
    (format t "~A~%" (metatilities:date-string ut)))
  (collect-length uts))

あんまりゴルフっぽくなりませんでした。 anarchy golf なら embed するところ。

1
2
3
4
5
6
(princ (loop for y from 2008 to 2013 sum
         (loop for m from 1 to 12
           as x = (encode-universal-time 0 0 0 13 m y)
           as a = (and (< (get-universal-time) x)
                       (= (elt (multiple-value-list (decode-universal-time x)) 6) 4))
           count a if a do (format t "~D-~D-13~%" y m))))

とりあえずナイーブなものを。

1
2
3
4
5
6
from datetime import*
d,n=date.today(),0
while d.year<2014:
 if d.day==13 and d.weekday()==4:print d;n+=1
 d+=timedelta(1)
print n

Lispといえば AI (Artificial Intelligence) ですが、今回はコンピュータの前にいる NI (Natural Intelligence) を使って解いてみました。ただ、信頼性がいまいち低いため、一部コンピュータにより補助を行い、精度を高めています。

実行例:

% gosh countup13fri.scm
(... 略 ...)


   December 2013
Su Mo Tu We Th Fr Sa
 1  2  3  4  5  6  7
 8  9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31

この月に13日の金曜日は含まれますか?
含まれる場合は y, 疲れたときは g, それ以外のときはリターンキーを押してください:
y
2009/2/13
2009/3/13
2009/11/13
2010/8/13
2011/5/13
2012/1/13
2012/4/13
2012/7/13
2013/9/13
2013/12/13
==> 10

※実行には外部コマンドの cal が必要です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#!/usr/bin/env gosh

(use gauche.process)

(let* ((now (sys-localtime (sys-time)))
       (lst '()))
  (do ((y #0=(+ 1900 (ref now 'year)) (+ y 1)))
      ((<= 2014 y)
       (dolist (elem (reverse lst))
         (apply format #t "~d/~d/13~%" elem))
       (format #t "==> ~d~%" (length lst)))
    (do ((m 0 (+ m 1)))
        ((<= 12 m))
      (unless (and (= #0# y)
                   (or (< m #1=(ref now 'mon))
                       (and (= #1# m) (< 13 (ref now 'mday)))))
        (let ((l (process-output->string-list `(cal ,(+ m 1) ,y))))
          (let loop ((ans (find #/8  9 10 11 12 13 14/ l)))
            (for-each print l)
            (print "この月に13日の金曜日は含まれますか?\n含まれる場合は y, 疲れたときは g, それ以外のときはリターンキーを押してください: ")
            (case (string->symbol (read-line))
              ((y Y) (if ans
                         (push! lst (list y (+ m 1)))
                         #2=(begin
                              (print "\n*** 本当ですか? もう一度よく確認してみてください ***\n")
                              (loop ans))))
              ((g G) (error "out of patience, aborting ..."))
              (else
               (when ans
                 #2#)))))))))
文章では20013年とあり、プログラムは2013年となっていますがどちらでしょう。

ぎゃー、すみません。 2013年の間違いです。 修正させていただきました。

2013年と都合よく解釈しました。

私にはこのくらいが限度のようです。

1
with(as.POSIXlt(l<-(seq(Sys.Date(),as.Date("2013-12-31"),"d"))), length(print(l[mday==13 & wday==5])))

短くしてみました。

1
l=Sys.Date():16070;class(l)="Date";length(print(l[format(l,"%d%w")==135]))

	
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
var counter=0;
var today = new Date();
for(var y=2008;y<2014;y++){
    for(var m=0;m<12;m++){
        var d13= new Date(y,m,13);
        if(d13 < today) continue;
        if(d13.getDay()==5){
            print(d13);
            counter++;
        }
    }
}
print(counter)

PHPがなかったので。 あまりうまく書けませんでしたが。

1
2
3
for($i=new DateTime;$i<=new DateTime('2013-12-31');$i->modify('+1 day')){
 if($i->format('wd')==513){$s.=$i->format('Y-m-d ');++$n;}
}echo$s,$n;

http://ja.doukaku.org/comment/7029/ だいらさんの投稿を参考に少し縮めました。まだ負けてますが、マジックナンバーぽいのが少ないと言うことで。

1
<?for($i=time();$i<mktime(0,0,0,1,1,14);$i+=86400)$n+=date(wd,$i)-513?0:print date(YMj_,$i);echo$n;

 素直に書いてみましたが,長いですね。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
import    java.util.Calendar
import    java.text.SimpleDateFormat

class Friday13(from:Calendar) {
    
    var    f13:List[Calendar] = null
    val    to:Calendar = Calendar.getInstance
    to.setTime((new SimpleDateFormat("yyyy/MM/dd")).parse("2014/01/01"))
    
    def this() = this(Calendar.getInstance)
    
    def search:List[Calendar] = {
        val    d:Calendar = from.clone.asInstanceOf[Calendar]
        if (d.get(Calendar.DAY_OF_MONTH) > 13) {
            d.set(Calendar.DAY_OF_MONTH,1)
            d.add(Calendar.MONTH,1)
        }
        def search(l:List[Calendar],d:Calendar,to:Calendar):List[Calendar] = {
            d.before(to) match {
                case false => l
                case _ => {
                    val    n = d.clone.asInstanceOf[Calendar]
                    n.add(Calendar.MONTH,1)
                    (d.get(Calendar.DAY_OF_WEEK) == Calendar.FRIDAY) match {
                        case true => search(l+d,n,to)
                        case _ => search(l,n,to)
                    }
                }
            }
        }
        d.set(Calendar.DAY_OF_MONTH,13)
        f13 = search(List(),d,to)
        f13
    }
    
    def print:Unit = {
        def print(l:List[Calendar]):Unit = l match {
            case List() => ()
            case d::rest => {
                Console.printf("%04d/%02d/%02d\n",d.get(Calendar.YEAR),d.get(Calendar.MONTH) + 1,d.get(Calendar.DAY_OF_MONTH))
                print(rest)
            }
        }
        print(f13)
        Console.printf("%dday matched\n",f13.size)
    }
}

object Main extends Application {
    try {
        val    f:Friday13 = new Friday13(Calendar.getInstance)
        f.search
        f.print
    } catch {
        case e:Exception => e.printStackTrace
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
private import std.stdio, std.date;

enum Friday = 5;

void main(){
    auto count = 0;
    foreach(day; Day(getUTCtime) .. MakeDay(2014, 1, 1)) {
        auto date = MakeDate(day, 0);
        if(WeekDay(date) == Friday && DateFromTime(date) == 13) {
            writefln("%04d-%02d-%02d", YearFromTime(date), MonthFromTime(date) + 1, DateFromTime(date));
            ++count;
        }
    }
    writeln(count);
}

 少し短くしてみました。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import    java.util.Calendar
import    java.text.SimpleDateFormat

class Friday13 {
    
    val    to:Calendar = Calendar.getInstance
    to.setTime((new SimpleDateFormat("yyyy/MM/dd")).parse("2013/12/13"))
    
    def print:Unit = {
        def search(l:List[Calendar],d:Calendar):List[Calendar] = Calendar.getInstance().after(d) match {
            case true => l
            case _ => {
                val    n:Calendar = d.clone.asInstanceOf[Calendar]
                n.add(Calendar.MONTH,-1)
                (d.get(Calendar.DAY_OF_WEEK) == Calendar.FRIDAY) match {
                    case true => search(d::l,n)
                    case _ => search(l,n)
                }
            }
        }
        Console.printf("%dday matched\n",(search(List(),to).foldLeft(0) { (c,d) => Console.printf("%04d/%02d/%02d\n",d.get(Calendar.YEAR),d.get(Calendar.MONTH)+1,d.get(Calendar.DAY_OF_MONTH)); c + 1 }))
    }
}

object Main extends Application {
    try {
        (new Friday13).print
    } catch {
        case e:Exception => e.printStackTrace
    }
}
C#は捻らずに素直に書いた方が短くなりますね。
1
using System;class P{static void Main(){DateTime d=DateTime.Now;int c=0;for(;d<=new DateTime(2013,12,31);d=d.AddDays(1)){if(d.Day==13&(int)(d.DayOfWeek)==5){Console.WriteLine(d);c++;}}Console.Write(c);}}

まずはfor文で。誰かLINQ版を書こうよ。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
const int WEEK = 7;
DateTime today = DateTime.Now.Date;
DateTime from = today.AddDays((DayOfWeek.Friday - today.DayOfWeek + WEEK) % WEEK);
DateTime to = new DateTime(2013, 12, 31);
int count = 0;
for (DateTime d = from; d <= to; d = d.AddDays(WEEK))
{
    if (d.Day == 13)
    {
        Console.WriteLine(d.ToShortDateString());
        count++;
    }
}
Console.WriteLine("{0} days", count);

実はLINQも試したのですが、長くなったので投稿しませんでした。 LINQで抽出する対象のDateTimeのコレクションを作る為に結局ループを回しちゃってるんですよね。

1
using System;using System.Collections.Generic;using System.Linq;class C{static void Main(){List<DateTime>l=new List<DateTime>();for(DateTime d=DateTime.Now;d<=new DateTime(2013,12,31);d=d.AddDays(1)){l.Add(d);}var v=from f in l where(int)(f.DayOfWeek)==5&f.Day==13select f.Date;foreach(var d in v){Console.WriteLine(d);}Console.Write(v.Count<DateTime>());}}

LLでないですし、ぜんぜん短くありませんし(ry

えーっと。ほとんどCです。

「今日から」の部分が一番面倒くさいことになってしまいました。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <ctime>
#include <iostream>

int dayOfWeek(int y, int m, int d)
{
    if((m == 1) || (m == 2))
    {
        y--;
        m += 12;
    }
    return (y + y / 4 - y / 100 + y / 400 + (13 * m + 8) / 5 + d) % 7;
}

int main(int, char* [])
{
    std::time_t today   = std::time(0);
    std::tm*    todayTm = std::localtime(&today);

    int count = 0;
    int m     = (todayTm->tm_mday <= 13) ? todayTm->tm_mon : todayTm->tm_mon + 1;
    for(int y = todayTm->tm_year + 1900; y <= 2013; ++y)
    {
        while(m <= 12)
        {
            if(dayOfWeek(y, m ,13) == 5)
            {
                std::cout << y << "/" << m << "/13" << std::endl;
                ++count;
            }
            ++m;
        }
        m = 1;
    }
    std::cout << "total: " << count << std::endl;

    return 0;
}

Javaで頑張って短くしてみました。 自分はこれが限界・・・。

1
import java.util.*;public class P{public static void main(String[]s){int c=0;for(Calendar a=Calendar.getInstance();a.get(1)<2014;a.add(5,1)){if(a.get(5)==13&&a.get(7)==6){System.out.println(a.getTime());c++;}}System.out.print(c);}}

public class P の publicを消して、224バイト。 これがJavaの限界かな?と1日考えた自分の結論です。

括弧が一組削れます。これで222バイト。

1
import java.util.*;class P{public static void main(String[]s){int c=0;for(Calendar a=Calendar.getInstance();a.get(1)<2014;a.add(5,1))if(a.get(5)==13&&a.get(7)==6){System.out.println(a.getTime());c++;}System.out.print(c);}}
ツェラーの公式を変形して。「今日の日付」はコマンドライン引数でもらいます。

$ ./fri13 2008 8 7
'09年2月13日
'09年3月13日
'09年11月13日
'10年8月13日
'11年5月13日
'12年1月13日
'12年4月13日
'12年7月13日
'13年9月13日
'13年12月13日
総数:10個
$
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
#include <stdio.h>
#include <stdlib.h>

int main(int c, char *v[]) {
  int n = 0, y, m, y0 = atoi(v[1])-2000, m0 = atoi(v[2]);
  if (m0 < 3) { y0--; m0 += 12; }
  for (y = y0; y < 14; y++)
    for (m = 3; m < 15; m++) {
      if (y == y0 && (m < m0 || (m == m0 && atoi(v[3]) > 13))) continue;
      if (!((26*(m+1)/10+y+y/4)%7)) {
        printf("'%02d年%d月13日\n", (m>=13)?y+1:y, (m>=13)?m-12:m);
        n++;
      }
    }
  printf("総数:%d個\n", n);

  return 0;
}

LINQのみ。 もうちょっと書きようがあるかなぁ?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
DateTime today = DateTime.Now.Date;
var q = from year in Enumerable.Range(today.Year, 2014 - today.Year)
        from month in Enumerable.Range(1, 12)
        let d = new DateTime(year, month, 13)
        where (d.DayOfWeek == DayOfWeek.Friday) && (d >= today)
        select d;
foreach (DateTime d in q)
{
    Console.WriteLine("{0:yyyy/MM/dd(ddd)}", d);
}
Console.WriteLine(q.Count());
}
おぉ!これがやりたかったんですよ。
勉強になりました。
1
2
<?for(;1388415600>$t=strtotime($i++."day");$c+=date(dw,$t)-135?0:print date("Y-m-d
",$t));echo$c;

ワンライナで157バイト。かなり行儀悪いです。

1
perl -MClass::Date=now,date -e"$d=now;$d+=[0,$d->day>13,13-$d->day];$\=\"\n\";until($d>date '2013-12-31'){++$_&&print$d->ymd if$d->wday==6;$d+='0-1-0'}print"
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
using System;
using System.Linq;

class Program
{
    static void Main()
    {
        var d =
            from n in Enumerable.Range(0, (new DateTime(2013, 12, 31) - DateTime.Today).Days)
            where DateTime.Today.AddDays(n).Day == 13
            where DateTime.Today.AddDays(n).DayOfWeek == DayOfWeek.Friday
            select DateTime.Today.AddDays(n);

        foreach (var item in d)
        {
            Console.WriteLine(item.ToShortDateString());
        }
        Console.WriteLine(d.Count());
    }
}