Functional\take & drop can work on infinite lists
authorwidmogrod <widmogrod@gmail.com>
Thu, 21 Dec 2017 16:55:28 +0000 (17:55 +0100)
committerwidmogrod <widmogrod@gmail.com>
Thu, 21 Dec 2017 16:55:28 +0000 (17:55 +0100)
src/Functional/listt.php
src/Functional/sublist.php
test/Functional/TakeTest.php

index a4b4b9d..76becbd 100644 (file)
@@ -5,7 +5,6 @@ namespace Widmogrod\Functional;
 use Widmogrod\FantasyLand\Foldable;
 use Widmogrod\Primitive\Listt;
 use Widmogrod\Primitive\ListtCons;
-use Widmogrod\Primitive\ListtNil;
 
 const fromIterable = 'Widmogrod\Functional\fromIterable';
 
@@ -77,6 +76,9 @@ function fromSnapshotIterator(SnapshotIterator $i): Listt
     });
 }
 
+/**
+ * @var callable
+ */
 const fromValue = 'Widmogrod\Functional\fromValue';
 
 function fromValue($value): Listt
@@ -88,7 +90,7 @@ function fromValue($value): Listt
 
 function fromNil(): Listt
 {
-    return new ListtNil();
+    return ListtCons::mempty();
 }
 
 /**
@@ -124,6 +126,9 @@ function concat(Foldable $xs)
     }, fromNil(), $xs);
 }
 
+/**
+ * @var callable
+ */
 const prepend = 'Widmogrod\Functional\prepend';
 
 /**
@@ -140,6 +145,9 @@ function prepend($x, Listt $xs = null)
     })(...func_get_args());
 }
 
+/**
+ * @var callable
+ */
 const append = 'Widmogrod\Functional\append';
 
 /**
@@ -164,6 +172,11 @@ function append(Listt $a, Listt $b = null)
 }
 
 /**
+ * @var callable
+ */
+const head = 'Widmogrod\Functional\head';
+
+/**
  * head :: [a] -> a
  *
  * Extract the first element of a list, which must be non-empty.
@@ -178,6 +191,11 @@ function head(Listt $l)
 }
 
 /**
+ * @var callable
+ */
+const tail = 'Widmogrod\Functional\tail';
+
+/**
  * tail :: [a] -> [a]
  *
  * Extract the elements after the head of a list, which must be non-empty.
@@ -192,6 +210,11 @@ function tail(Listt $l)
 }
 
 /**
+ * @var callable
+ */
+const length = 'Widmogrod\Functional\length';
+
+/**
  * length :: Foldable t => t a -> Int
  *
  * Returns the size/length of a finite structure as an Int.
index ecfcb95..ef4ec60 100644 (file)
@@ -6,6 +6,11 @@ use Widmogrod\Primitive\EmptyListError;
 use Widmogrod\Primitive\Listt;
 
 /**
+ * @var callable
+ */
+const take = 'Widmogrod\Functional\take';
+
+/**
  * take :: Int -> [a] -> [a]
  *
  * take n, applied to a list xs, returns the prefix of xs of length n, or xs itself if n > length xs:
@@ -21,10 +26,6 @@ function take(int $n, Listt $xs = null)
             return fromNil();
         }
 
-        if ($n > length($xs)) {
-            return $xs;
-        }
-
         try {
             return prepend(head($xs), take($n - 1, tail($xs)));
         } catch (EmptyListError $e) {
@@ -34,6 +35,11 @@ function take(int $n, Listt $xs = null)
 }
 
 /**
+ * @var callable
+ */
+const drop = 'Widmogrod\Functional\drop';
+
+/**
  * drop :: Int -> [a] -> [a]
  *
  * drop n xs returns the suffix of xs after the first n elements, or [] if n > length xs:
@@ -48,10 +54,6 @@ function drop(int $n, Listt $xs = null)
             return $xs;
         }
 
-        if ($n > length($xs)) {
-            return fromNil();
-        }
-
         try {
             return drop($n - 1, tail($xs));
         } catch (EmptyListError $e) {
@@ -61,16 +63,32 @@ function drop(int $n, Listt $xs = null)
 }
 
 /**
+ * @var callable
+ */
+const splitAt = 'Widmogrod\Functional\splitAt';
+
+/**
  * splitAt :: Int -> [a] -> ([a], [a])
  *
  * splitAt n xs returns a tuple where first element is xs prefix of length n and second element is the remainder of the list:
+ *
+ * @param int $n
+ * @param Listt $xs
+ * @return array
  */
-function splitAt()
+function splitAt(int $n, Listt $xs = null)
 {
-    // TODO
+    return curryN(2, function (int $n, Listt $xs): array {
+        return [take($n, $xs), drop($n, $xs)];
+    })(...func_get_args());
 }
 
 /**
+ * @var callable
+ */
+const takeWhile = 'Widmogrod\Functional\takeWhile';
+
+/**
  * takeWhile :: (a -> Bool) -> [a] -> [a]
  *
  * takeWhile, applied to a predicate p and a list xs, returns the longest prefix (possibly empty) of xs of elements that satisfy p:
@@ -81,6 +99,11 @@ function takeWhile()
 }
 
 /**
+ * @var callable
+ */
+const dropWhile = 'Widmogrod\Functional\dropWhile';
+
+/**
  * dropWhile :: (a -> Bool) -> [a] -> [a]
  *
  * dropWhile p xs returns the suffix remaining after takeWhile p xs:
@@ -91,6 +114,11 @@ function dropWhile()
 }
 
 /**
+ * @var callable
+ */
+const span = 'Widmogrod\Functional\span';
+
+/**
  * span :: (a -> Bool) -> [a] -> ([a], [a])
  *
  * span, applied to a predicate p and a list xs, returns a tuple where first element is longest prefix (possibly empty) of xs of elements that satisfy p and second element is the remainder of the list:
@@ -101,6 +129,11 @@ function span()
 }
 
 /**
+ * @var callable
+ */
+const breakk = 'Widmogrod\Functional\breakk';
+
+/**
  * break :: (a -> Bool) -> [a] -> ([a], [a])
  *
  * break, applied to a predicate p and a list xs, returns a tuple where first element is longest prefix (possibly empty) of xs of elements that do not satisfy p and second element is the remainder of the list:
index 862f926..149e02e 100644 (file)
@@ -5,6 +5,7 @@ namespace test\Functional;
 use Widmogrod\Primitive\Listt;
 use function Widmogrod\Functional\fromIterable;
 use function Widmogrod\Functional\fromNil;
+use function Widmogrod\Functional\repeat;
 use function Widmogrod\Functional\take;
 
 class TakeTest extends \PHPUnit_Framework_TestCase
@@ -23,8 +24,7 @@ class TakeTest extends \PHPUnit_Framework_TestCase
         $e = print_r($expected->extract(), true);
 
         $this->assertTrue(
-            $result->equals($expected),
-            "$e != $r"
+            $result->equals($expected)
         );
     }
 
@@ -56,6 +56,11 @@ class TakeTest extends \PHPUnit_Framework_TestCase
                 '$n' => 3000,
                 '$expected' => fromIterable([1, 2, 3, 4, 5]),
             ],
+            'should return part of infinite list' => [
+                '$a' => repeat('a'),
+                '$n' => 3,
+                '$expected' => fromIterable(['a', 'a', 'a']),
+            ],
         ];
     }
 }