Yone
Yone

Reputation: 2134

Interface as argument, returning result

I am doing the following programming exercise: Moves in squared strings (I). The statement is:

You are given a string of n lines, each substring being n characters long: For example:

s = "abcd\nefgh\nijkl\nmnop"

We will study some transformations of this square of strings.

Vertical mirror: vert_mirror (or vertMirror or vert-mirror)

vert_mirror(s) => "dcba\nhgfe\nlkji\nponm"

Horizontal mirror: hor_mirror (or horMirror or hor-mirror)

hor_mirror(s) => "mnop\nijkl\nefgh\nabcd"

or printed:

vertical mirror |horizontal mirror abcd --> dcba |abcd --> mnop efgh hgfe |efgh ijkl ijkl lkji |ijkl
efgh mnop ponm |mnop abcd

Task:

Write these two functions

and

high-order function oper(fct, s) where
    fct is the function of one variable f to apply to the string s (fct will be one of vertMirror, horMirror)

Examples:

s = "abcd\nefgh\nijkl\nmnop" oper(vert_mirror, s) => "dcba\nhgfe\nlkji\nponm" oper(hor_mirror, s) => "mnop\nijkl\nefgh\nabcd"

I would like some help because of it is the first time I need to pass functions as method's arguments. The initial test cases are (taken from the exercise):

import static org.junit.Assert.*;
import org.junit.Test;

public class OpstringsTest {

    private static void testing(String actual, String expected) {
        assertEquals(expected, actual);
    }
    @Test
    public void test() {
        System.out.println("Fixed Tests vertMirror");
        String s = "hSgdHQ\nHnDMao\nClNNxX\niRvxxH\nbqTVvA\nwvSyRu";
        String r = "QHdgSh\noaMDnH\nXxNNlC\nHxxvRi\nAvVTqb\nuRySvw";
        testing(Opstrings.oper(Opstrings::vertMirror, s), r);
        s = "IzOTWE\nkkbeCM\nWuzZxM\nvDddJw\njiJyHF\nPVHfSx";
        r = "EWTOzI\nMCebkk\nMxZzuW\nwJddDv\nFHyJij\nxSfHVP";
        testing(Opstrings.oper(Opstrings::vertMirror, s), r);

        System.out.println("Fixed Tests horMirror");
        s = "lVHt\nJVhv\nCSbg\nyeCt";
        r = "yeCt\nCSbg\nJVhv\nlVHt";        
        testing(Opstrings.oper(Opstrings::horMirror, s), r);
        s = "njMK\ndbrZ\nLPKo\ncEYz";
        r = "cEYz\nLPKo\ndbrZ\nnjMK";
        testing(Opstrings.oper(Opstrings::horMirror, s), r);
    }
}

I have tried the following implementation:

import java.util.function.*;
class Opstrings {    
  public static String vertMirror (String string) {
    String[] words = string.split("\\s+");
    StringBuilder sb = new StringBuilder();
    for(String word : words){
      sb.append(new StringBuilder(word).reverse()+"\n");
    }
    return string.strip().toString();
  }
  public static String horMirror (String string) {
    return new StringBuilder(string).reverse().toString();
  }
  public static String oper/*🅾️*/(Consumer<String> operator, String s) {
    return operator.accept(s);
  }
}

And when we execute the previous code, the compiler outputs:

./src/main/java/Opstrings.java:15: error: incompatible types: void cannot be converted to String return operator.accept(s); ^ 1 error

I understand that Consumer is defined to return void. So then I decided to investigate and I read that Callable is able to return results. So then I tried:

import java.util.concurrent.Callable;
class Opstrings {    
  public static String vertMirror (String string) {
    String[] words = string.split("\\s+");
    StringBuilder sb = new StringBuilder();
    for(String word : words){
      sb.append(new StringBuilder(word).reverse()+"\n");
    }
    return string.strip().toString();
  }
  public static String horMirror (String string) {
    return new StringBuilder(string).reverse().toString();
  }
  public static String oper/*🅾️*/(Callable operator, String s) {
    return operator.accept(s);
  }
}

However this time our compiler says that we can not call the functions without arguments when String is required (from the test cases):

./src/test/java/OpstringsTest.java:14: error: incompatible types: invalid method reference testing(Opstrings.oper(Opstrings::vertMirror, s), r); ^ method vertMirror in class Opstrings cannot be applied to given types required: String found: no arguments reason: actual and formal argument lists differ in length ./src/test/java/OpstringsTest.java:17: error: incompatible types: invalid method reference

In addition, I have also read:

Could you help me please figuring out how could we pass in Functional Interfaces as method's arguments and return its results?

EDIT: As @JB Nizet says it can be done using UnaryOperator:

import java.util.function.*;

class Opstrings {    
  public static String vertMirror (String string) {
    String[] words = string.split("\\s+");
    for(int i = 0; i < words.length; i++){
      words[i] = new StringBuilder(words[i]).reverse().toString();
    }
    return String.join("\n",words);
  }

  public static String horMirror (String string) {
    String[] words = string.split("\\s+");
    StringBuilder sb = new StringBuilder();
    for(int i = words.length-1; i >= 0; i--){
      sb.append(words[i]+"\n");
    }
    return sb.toString().strip();
  }

  public static String oper/*🅾️*/(UnaryOperator<String> operator, String s) {
    return operator.apply(s);
  }
}

Upvotes: 1

Views: 260

Answers (1)

JB Nizet
JB Nizet

Reputation: 691765

Consumer is a function from someting to nothing (void). Callable is a function from nothing to something. You want a function from something to something else.

That's what Function is for. Or, since the return type and the argument type are the same in your case, UnaryOperator.

Upvotes: 3

Related Questions