Создание цепочек твитов из String в Java

Я недавно получил этот вопрос на экзамене, вот как я реализовал, он работает нормально. Однако я хотел бы знать, можно ли улучшить код, поскольку я думаю, что его обязательно нужно улучшить. Вопрос был такой:

Раньше Твиттер имел ограничение в 140 символов на твит. Если вы хотели ввести более длинные твиты, вам нужно было разбить его на несколько. Мы хотим создать функцию, которая получает строку, которая является содержимым твита, которая возвращает все твиты с пронумерованными префиксами (список). Неважно, если слова разделены. Примерно так: (Tweet 1/5) tweetcontent …. (Tweet 2/5) tweetcontent …

public class Exercise {

static int MAX_CHARACTERS_OF_MESSAGE_IN_TWEET = 129;

public List<String> tweets(String message) {
    List<String> tweetsToReturn = new ArrayList<>();
    //calculate numberOfTweets needed to tweet
    int numberOfTweets = message.length() / MAX_CHARACTERS_OF_MESSAGE_IN_TWEET;
    //check if we need to add another tweet for the latest characters of the message
    int restOfTWeet = message.length() % MAX_CHARACTERS_OF_MESSAGE_IN_TWEET;
    if (restOfTWeet != 0) {
        numberOfTweets++;
    }
    //if there is only one tweet
    if (numberOfTweets == 1) {
        tweetsToReturn.add(message);
    } else { //if there is more than one tweet
        for (int i = 0; i < numberOfTweets - 1; i++) {
            tweetsToReturn.add("(Tweet " + (i + 1) + "/" + numberOfTweets + ")" + message.substring(i * MAX_CHARACTERS_OF_MESSAGE_IN_TWEET, (i+1) * MAX_CHARACTERS_OF_MESSAGE_IN_TWEET));
        }
        //create the latest tweet
        if (restOfTWeet != 0) {
            tweetsToReturn.add("(Tweet " + numberOfTweets + "/" + numberOfTweets + ")" + message.substring((numberOfTweets - 1) * MAX_CHARACTERS_OF_MESSAGE_IN_TWEET));
        }
    }
    return tweetsToReturn;
}}

Бонусный балл: сделайте это сейчас, но без разделения слов. (Этого мне сделать не удалось).

Спасибо!!!

1 ответ
1

В целом ваш код выполняет свою работу правильно и делает это не излишне сложным способом. Молодец. Остальные мои комментарии касаются того, как вы можете сделать код немного легче для чтения, потому что чем проще его читать, тем легче будет решить проблему с бонусом.

Для начала, когда вы делитесь кодом с другими, вы должны запустить автоформат в своей среде IDE и включить необходимый импорт.

MAX_CHARACTERS_OF_MESSAGE_IN_TWEET слишком длинный, я бы предпочел MAX_TWEET_LENGTH и это должно быть отмечено final. Значение должно отражать знание домена, т. Е. Длину 140 символов. Затем вы можете показать, как вы добрались до 129:

    static final int MAX_TWEET_LENGTH = 140;
    static final int PREFIX_LENGTH = 11;
    static final int CHARS_PER_TWEET = MAX_TWEET_LENGTH - PREFIX_LENGTH;
public List<String> tweets(String message) {

Он должен быть помечен как статический, потому что он не использует никаких переменных экземпляра. Это делает его доступным для потребителей без необходимости new Exercise(). И это делает код немного легче читаемым для людей, читающих код, потому что они знают, что им не нужно рассматривать какие-либо переменные, определенные в другом месте, за исключением других static переменные (которые обычно final константы, которые информативны).

Переменные следует объявлять рядом с тем местом, где они используются впервые, поэтому List<String> tweetsToReturn должен прийти позже. Переменные, которые вы собираетесь вернуть, часто принимают имя самого метода, и поэтому toReturn часть подразумевается и может быть опущена.

        // if there is only one tweet

Этот комментарий не нужен – он просто говорит то, что уже сказано в коде. Большинство комментариев попадают в эту категорию. Как только вы “свободно” владеете “чтением” кода, это становится очевидным. Это как если бы вы что-то написали двуязычному человеку, но дважды сказали одно и то же, по одному на каждом языке.

        // if there is more than one tweet

То же самое с этим комментарием, но я добавлю, что иногда лучше избегать else блокирует полностью в тех случаях, когда if блок может просто return вне функции. Вы можете использовать Arrays.asList просто вернуть массив, если все значения уже известны. В этом случае будет только одно значение, и мы его уже знаем (message).

Если вы вычисляете значение, проверяете его один раз и больше не используете, вам не нужно создавать для него имя переменной (restofTWeet), просто поместите его “inline”. Здесь вы снова используете значение позже, но я чувствую, что мы можем внести изменения, которые на самом деле не понадобятся, поэтому я бы все еще встраивал его сейчас.

Чтобы создать список твитов, вы используете for loop, который работает в тех случаях, когда мы знаем, сколько раз мы будем повторять цикл, и мы действительно знаем. Но требуется больше умственных усилий, чтобы убедиться, что мы правильно рассчитали количество циклов, по сравнению с тем, если бы мы просто настроили простой «цикл до завершения», используя while, поэтому я бы использовал while и это, как оказалось, немного облегчило задачу с бонусом.

Что касается создания твитов, как у вас с + on Strings, это хорошо с точки зрения производительности и не так уж плохо с точки зрения читаемости, но вы должны знать о других способах написания того же самого, которые вы можете иногда предпочесть, если это делает его еще более легким для чтения. В частности, также следует знать, как использовать String.format и StringBuilder, у которых есть свои плюсы и минусы. Я использовал оба в приведенных ниже примерах.

Вот версия после указанных выше изменений, но все еще решающая исходную проблему:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Exercise {

    static final int MAX_TWEET_LENGTH = 140;
    static final int PREFIX_LENGTH = 11;
    static final int CHARS_PER_TWEET = MAX_TWEET_LENGTH - PREFIX_LENGTH;

    public static List<String> tweets(String message) {
        if (message.length() <= MAX_TWEET_LENGTH) {
            return Arrays.asList(message);
        }
        
        int tweetCount = message.length() / CHARS_PER_TWEET;
        if (message.length() % CHARS_PER_TWEET != 0) {
            tweetCount++;
        }

        List<String> tweets = new ArrayList<>();
        int cursor = 0;
        int currentCount = 1;
        while (cursor < message.length()) {
            final int contentLength = Math.min(message.length() - cursor,
                    CHARS_PER_TWEET);
            final String content = message.substring(cursor, cursor + contentLength);
            tweets.add(String.format("(Tweet %d/%d)%s", currentCount, tweetCount, content));
            cursor += contentLength;
            currentCount++;
        }
        return tweets;
    }

}

Тогда для бонусных баллов мы можем получить массив слов, используя String.split, и вместо того, чтобы вычислить, сколько символов мы собираемся добавить в цикл, мы выполняем внутренний цикл, добавляя слова из списка до тех пор, пока следующее слово не подойдет или не кончится больше слов:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Exercise {

    static final int MAX_TWEET_LENGTH = 140;
    static final int PREFIX_LENGTH = 11;
    static final int CHARS_PER_TWEET = MAX_TWEET_LENGTH - PREFIX_LENGTH;

    public static List<String> tweets(String message) {
        if (message.length() <= MAX_TWEET_LENGTH) {
            return Arrays.asList(message);
        }

        int tweetCount = message.length() / CHARS_PER_TWEET;
        if (message.length() % CHARS_PER_TWEET != 0) {
            tweetCount++;
        }

        String[] words = message.split("\s");
        
        List<String> tweets = new ArrayList<>();
        int cursor = 0;
        int currentCount = 1;

        while (cursor < words.length) {
            StringBuilder content = new StringBuilder(CHARS_PER_TWEET);
            while (cursor < words.length
                    && content.length() + words[cursor].length() + 1 < CHARS_PER_TWEET) {
                content.append(" ");
                content.append(words[cursor]);
                cursor++;
            }
            tweets.add(
                    String.format("(Tweet %d/%d)%s", currentCount, tweetCount, content));
            currentCount++;
        }
        return tweets;
    }

}

И простой метод проверки:

    public static void main(String[] args) {
        List<String> tweets = tweets(
                "It was the best of times, it was the worst of times, it was the age of " + 
                "wisdom, it was the age of foolishness, it was the epoch of belief, it was " + 
                "the epoch of incredulity, it was the season of Light, it was the season of " + 
                "Darkness, it was the spring of hope, it was the winter of despair, we had " + 
                "everything before us, we had nothing before us, we were all going direct " + 
                "to Heaven, we were all going direct the other way—in short, the period " + 
                "was so far like the present period, that some of its noisiest authorities " + 
                "insisted on its being received, for good or for evil, in the superlative " + 
                "degree of comparison only.");
        for (String tweet : tweets) {
            System.out.println(tweet);
        }
    }

Выход:

(Tweet 1/5) It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch
(Tweet 2/5) of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of
(Tweet 3/5) hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven,
(Tweet 4/5) we were all going direct the other way—in short, the period was so far like the present period, that some of its noisiest
(Tweet 5/5) authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.

(Мы предполагали, что количество твитов будет меньше 10, меньше 100 и т. Д. Проблема становится немного сложнее, если это не так, потому что количество символов, которые вы можете использовать в твите, зависит от количества в префиксе, который зависит от общего количества твитов, которое зависит от количества символов, используемых в твите. Циклическая зависимость. Таким образом, решение может включать попытку построения с одной длиной префикса, и когда длина префикса изменяется, запускать все еще раз с новой длиной префикса и повторяйте, пока не пройдете весь ввод.

На самом деле, в моем коде для проблемы с бонусом есть ошибка, связанная с той же проблемой, потому что я все еще использую CHARS_PER_TWEET для подсчета количества твитов, но иногда твиты короче, чем те, которые основаны на разделении слов. Чтобы исправить это, вам нужно будет вычислить содержимое всех твитов без префикса, затем вернуться и добавить префикс, содержащий правильное количество. Затем аналогичным образом, если длина префикса изменяется в зависимости от количества твитов, повторите все заново.)

  • Вау! Большое спасибо за этот ответ, Джереми! Это супер полный! Я попробую сейчас и посмотрю, смогу ли я исправить эту ошибку, которую вы комментируете по проблеме с бонусом. Ты жжешь!!!

    – Исанма

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *