Repository: sanctuary-js/sanctuary
Pull request #630 (2019-04-26 13:19:18)

array: generalize S.takeLast and S.dropLast
The implementations are not as efficient as they could be, but I want to avoid spreading complexity currently confined to `_takeDrop`.



Patch:
diff --git a/index.js b/index.js
index e900141..5138669 100644
--- a/index.js
+++ b/index.js
@@ -3003,59 +3003,69 @@
     impl: drop
   };
 
-  //# takeLast :: Integer -> Array a -> Maybe (Array a)
+  //# takeLast :: (Applicative f, Foldable f, Monoid (f a)) => Integer -> f a -> Maybe (f a)
   //.
-  //. Returns Just the last N elements of the given array if N is greater
-  //. than or equal to zero and less than or equal to the length of the array;
+  //. Returns Just the last N elements of the given structure if N is
+  //. non-negative and less than or equal to the size of the structure;
   //. Nothing otherwise.
   //.
   //. ```javascript
-  //. > S.takeLast (2) (['a', 'b', 'c', 'd', 'e'])
-  //. Just (['d', 'e'])
+  //. > S.takeLast (0) (['foo', 'bar'])
+  //. Just ([])
   //.
-  //. > S.takeLast (5) (['a', 'b', 'c', 'd', 'e'])
-  //. Just (['a', 'b', 'c', 'd', 'e'])
+  //. > S.takeLast (1) (['foo', 'bar'])
+  //. Just (['bar'])
+  //.
+  //. > S.takeLast (2) (['foo', 'bar'])
+  //. Just (['foo', 'bar'])
   //.
-  //. > S.takeLast (6) (['a', 'b', 'c', 'd', 'e'])
+  //. > S.takeLast (3) (['foo', 'bar'])
   //. Nothing
+  //.
+  //. > S.takeLast (3) (Cons (1) (Cons (2) (Cons (3) (Cons (4) (Nil)))))
+  //. Just (Cons (2) (Cons (3) (Cons (4) (Nil))))
   //. ```
   function takeLast(n) {
     return function(xs) {
-      return n >= 0 && n <= xs.length ? Just (xs.slice (xs.length - n))
-                                      : Nothing;
+      return Z.map (Z.reverse, take (n) (Z.reverse (xs)));
     };
   }
   _.takeLast = {
-    consts: {},
-    types: [$.Integer, $.Array (a), $.Maybe ($.Array (a))],
+    consts: {f: [Z.Applicative, Z.Foldable, Z.Monoid]},
+    types: [$.Integer, f (a), $.Maybe (f (a))],
     impl: takeLast
   };
 
-  //# dropLast :: Integer -> Array a -> Maybe (Array a)
+  //# dropLast :: (Applicative f, Foldable f, Monoid (f a)) => Integer -> f a -> Maybe (f a)
   //.
-  //. Returns Just all but the last N elements of the given array if N is
-  //. greater than or equal to zero and less than or equal to the length of
-  //. the array; Nothing otherwise.
+  //. Returns Just all but the last N elements of the given structure if
+  //. N is non-negative and less than or equal to the size of the structure;
+  //. Nothing otherwise.
   //.
   //. ```javascript
-  //. > S.dropLast (2) (['a', 'b', 'c', 'd', 'e'])
-  //. Just (['a', 'b', 'c'])
+  //. > S.dropLast (0) (['foo', 'bar'])
+  //. Just (['foo', 'bar'])
+  //.
+  //. > S.dropLast (1) (['foo', 'bar'])
+  //. Just (['foo'])
   //.
-  //. > S.dropLast (5) (['a', 'b', 'c', 'd', 'e'])
+  //. > S.dropLast (2) (['foo', 'bar'])
   //. Just ([])
   //.
-  //. > S.dropLast (6) (['a', 'b', 'c', 'd', 'e'])
+  //. > S.dropLast (3) (['foo', 'bar'])
   //. Nothing
+  //.
+  //. > S.dropLast (3) (Cons (1) (Cons (2) (Cons (3) (Cons (4) (Nil)))))
+  //. Just (Cons (1) (Nil))
   //. ```
   function dropLast(n) {
     return function(xs) {
-      return n >= 0 && n <= xs.length ? Just (xs.slice (0, xs.length - n))
-                                      : Nothing;
+      return Z.map (Z.reverse, drop (n) (Z.reverse (xs)));
     };
   }
   _.dropLast = {
-    consts: {},
-    types: [$.Integer, $.Array (a), $.Maybe ($.Array (a))],
+    consts: {f: [Z.Applicative, Z.Foldable, Z.Monoid]},
+    types: [$.Integer, f (a), $.Maybe (f (a))],
     impl: dropLast
   };
 


Test patch:
diff --git a/test/dropLast.js b/test/dropLast.js
index 6562689..26c0ff6 100644
--- a/test/dropLast.js
+++ b/test/dropLast.js
@@ -1,7 +1,8 @@
 'use strict';
 
-const S = require ('..');
+const S = require ('./internal/sanctuary');
 
+const {Nil, Cons} = require ('./internal/List');
 const eq = require ('./internal/eq');
 
 
@@ -9,7 +10,7 @@ test ('dropLast', () => {
 
   eq (typeof S.dropLast) ('function');
   eq (S.dropLast.length) (1);
-  eq (S.show (S.dropLast)) ('dropLast :: Integer -> Array a -> Maybe (Array a)');
+  eq (S.show (S.dropLast)) ('dropLast :: (Applicative f, Foldable f, Monoid f) => Integer -> f a -> Maybe (f a)');
 
   eq (S.dropLast (0) ([1, 2, 3, 4, 5])) (S.Just ([1, 2, 3, 4, 5]));
   eq (S.dropLast (1) ([1, 2, 3, 4, 5])) (S.Just ([1, 2, 3, 4]));
@@ -21,4 +22,12 @@ test ('dropLast', () => {
 
   eq (S.dropLast (-1) ([1, 2, 3, 4, 5])) (S.Nothing);
 
+  eq (S.dropLast (0) (Cons (1) (Cons (2) (Cons (3) (Nil))))) (S.Just (Cons (1) (Cons (2) (Cons (3) (Nil)))));
+  eq (S.dropLast (1) (Cons (1) (Cons (2) (Cons (3) (Nil))))) (S.Just (Cons (1) (Cons (2) (Nil))));
+  eq (S.dropLast (2) (Cons (1) (Cons (2) (Cons (3) (Nil))))) (S.Just (Cons (1) (Nil)));
+  eq (S.dropLast (3) (Cons (1) (Cons (2) (Cons (3) (Nil))))) (S.Just (Nil));
+  eq (S.dropLast (4) (Cons (1) (Cons (2) (Cons (3) (Nil))))) (S.Nothing);
+
+  eq (S.dropLast (-1) (Cons (1) (Cons (2) (Cons (3) (Nil))))) (S.Nothing);
+
 });
diff --git a/test/takeLast.js b/test/takeLast.js
index e005971..37a1516 100644
--- a/test/takeLast.js
+++ b/test/takeLast.js
@@ -1,7 +1,8 @@
 'use strict';
 
-const S = require ('..');
+const S = require ('./internal/sanctuary');
 
+const {Nil, Cons} = require ('./internal/List');
 const eq = require ('./internal/eq');
 
 
@@ -9,7 +10,7 @@ test ('takeLast', () => {
 
   eq (typeof S.takeLast) ('function');
   eq (S.takeLast.length) (1);
-  eq (S.show (S.takeLast)) ('takeLast :: Integer -> Array a -> Maybe (Array a)');
+  eq (S.show (S.takeLast)) ('takeLast :: (Applicative f, Foldable f, Monoid f) => Integer -> f a -> Maybe (f a)');
 
   eq (S.takeLast (0) ([1, 2, 3, 4, 5])) (S.Just ([]));
   eq (S.takeLast (1) ([1, 2, 3, 4, 5])) (S.Just ([5]));
@@ -21,4 +22,12 @@ test ('takeLast', () => {
 
   eq (S.takeLast (-1) ([1, 2, 3, 4, 5])) (S.Nothing);
 
+  eq (S.takeLast (0) (Cons (1) (Cons (2) (Cons (3) (Nil))))) (S.Just (Nil));
+  eq (S.takeLast (1) (Cons (1) (Cons (2) (Cons (3) (Nil))))) (S.Just (Cons (3) (Nil)));
+  eq (S.takeLast (2) (Cons (1) (Cons (2) (Cons (3) (Nil))))) (S.Just (Cons (2) (Cons (3) (Nil))));
+  eq (S.takeLast (3) (Cons (1) (Cons (2) (Cons (3) (Nil))))) (S.Just (Cons (1) (Cons (2) (Cons (3) (Nil)))));
+  eq (S.takeLast (4) (Cons (1) (Cons (2) (Cons (3) (Nil))))) (S.Nothing);
+
+  eq (S.takeLast (-1) (Cons (1) (Cons (2) (Cons (3) (Nil))))) (S.Nothing);
+
 });