Это не ответ «сделай это, и все будет хорошо», потому что я не совсем понимаю, что ты пытаешься. Однако я надеюсь, что этот ответ включает достаточно указателей для создания кода, который вы ищете.
e 1
Вы абсолютно правильно используете функцию рабочего листа, а не свой собственный VBA, когда существует подходящая функция рабочего листа, поскольку функция рабочего листа будет значительно быстрее. Однако, если есть способ заставить функцию Max возвращать строку, я не знаю ее. Я считаю, что вам придется использовать VBA для сканирования каждого столбца.
Выпуск 2
On Error Resume Next
никогда не должны использоваться, как это, так как все ошибки будут игнорироваться. В идеале вы избегаете ошибок, проверяя заранее. Если вы хотите открыть файл, вы должны проверить его существо, прежде чем пытаться открыть, а не ждать, пока open завершится с ошибкой, и сообщите об ошибке. Однако есть ситуации, когда вы не можете проверить наличие ошибки. В таких ситуациях вы можете использовать On Error Resume Next
так:
Err.Clear
On Error Resume Next
' Statement that might fail
On Error GoTo 0
If Err.Number <> 0 Then
' Statement failed.
' Description of failure in Err.Description.
' Report error with user friendly message and exit or take corrective action.
End If
Выпуск 3
Пожалуйста отступы ваш код, чтобы он легче читать и обнаружить ошибки. Например:
1 Public Sub run_CalcPeakTemp()
2 Dim myCalRange As Range
3 Dim iReply As Integer
4 'On Error Resume Next
5 Set myCalcRange = Application.InputBox(Prompt:="Select first row and then Ctrl+Shift+down", _
6 Title:="Select Range", Type:=8)
7 myCalcRange.Select
8 If myCalcRange Is Nothing Then
9 iReply = MsgBox("Range not selected!")
10 Exit Sub
11 If myCalcRange Is notNothing Then
12 Call run_CalcPeakTemp
13 End If
14 End If
15 End Sub
Я добавил номера строк, чтобы я мог легко ссылаться на утверждения. Я также разделил строку 5 на две строки, поэтому большая часть из них видна.
Выпуск 4
В строке 2, вы объявляете myCalRange
. В другом месте в рутине вы используете myCalcRange
. Если первый оператор вашего модуля равен Option Explicit
, во время компиляции вам будет сообщено, что myCalcRange
не был объявлен. Если вы опустите Option Explicit
, первая ссылка на myCalcRange
будет выполнять неявное объявление. Обнаружение неявных деклараций может быть очень сложным. Всегда включайте Option Explicit
.
Выпуск 5
линия 11 должна быть If Not myCalcRange Is Nothing Then
. У VBA нет оператора IsNot, и место было не в том месте.
Выпуск 6
Я никогда не использовал InputBox таким образом, и я найти помощь в заблуждение:
Set MyRange = Application.InputBox (подсказка: = "Sample", введите: = 8)
Если вы не используете инструкцию Set, переменная устанавливается на значение в диапазоне, а не на объект Range.
Если myRange
объявлен как Range
то Set
является обязательным. Если myRange
объявлен как Variant
, то Set
запрещен. Если myRange
не объявлен, и вы полагаетесь на неявное объявление, то myRange
будет объявлен как Range
, если вы включите Set
и Variant
, если вы его опустите.
Это не ваша ошибка. Эта функция (?) VBA составляет не менее 11 лет, и я могу только предположить, что кто-то думал, что они полезны.
Выпуск 7
7 myCalcRange.Select
8 If myCalcRange Is Nothing Then
Вы не можете выбрать диапазон, нет ничего. Тест должен быть первым.
Выпуск 8
8 If myCalcRange Is Nothing Then
9 iReply = MsgBox("Range not selected!")
10 Exit Sub
11 If myCalcRange Is notNothing Then
12 Call run_CalcPeakTemp
13 End If
14 End If
С отступом вы можете увидеть, что весь этот код находится в пределах первого If
. Я не уверен, что это то, что вы хотели. Ты имел ввиду?
8 If myCalcRange Is Nothing Then
9 iReply = MsgBox("Range not selected!")
10 Exit Sub
14 End If
11 If myCalcRange Is notNothing Then
12 Call run_CalcPeakTemp
13 End If
Я предполагаю, что вы пытаетесь заставить пользователя выбрать диапазон. В общем, вы должны разрешить пользователю отменить выбор.В теории, чтобы заставить пользователя сделать выбор вам нужно что-то вроде:
Set myCalcRange = Nothing
Do While myCalcRange Is Nothing
Set myCalcRange = Application.InputBox ...
Loop
На практике InputBox не позволяют пользователю нажать OK, если не был выбран и нажав Отменить дает ряд Ошибка VBA. InputBox(... type := 8)
- это не утверждение, которое я когда-либо использовал!
Выпуск 9
12 Call run_CalcPeakTemp
Рутинные называющие себя называются рекурсия и разрешен VBA, но он не может быть использован таким образом. Одним из возможных вариантов использования является поиск иерархии и получение значения внизу. Процедура проверяет наличие в нижней части иерархии. Если это так, он возвращает значение. Если это не так, он называет себя следующим уровнем вниз как его параметр.
Это VBA эквивалент простого использования рекурсии меня учили много лет назад:
Function Factorial(ByVal N As Long) As Long
If N = 1 Then
Factorial = 1
Else
Factorial = N * Factorial(N - 1)
End If
End Function
Эта процедура:
Sub Test()
Debug.Print "Factorial(1) = " & Factorial(1)
Debug.Print "Factorial(2) = " & Factorial(2)
Debug.Print "Factorial(3) = " & Factorial(3)
Debug.Print "Factorial(4) = " & Factorial(4)
Debug.Print "Factorial(5) = " & Factorial(5)
End Sub
отображает следующие в немедленном окне:
Factorial(1) = 1
Factorial(2) = 2
Factorial(3) = 6
Factorial(4) = 24
Factorial(5) = 120
Некоторый код, который может быть полезен
Этот код соответствует моему предположению о вашем требовании.
Я немного говорю о синтаксисе VBA, который я использовал. В общем, как только вы знаете, что существует утверждение, его легко найти, но спросите, если это необходимо.
Я попытался объяснить, что я делаю. Я попытался сделать мой код общим и как можно более удобным, насколько я могу. Здесь много, но если вы медленно работаете над кодом, я считаю, что вы поймете, что делает каждый раздел. Снова спросите, если необходимо.
Я думаю, что обучение программированию - это как научиться управлять автомобилем. В конце вашего первого урока вы знаете, что никогда не сможете работать с тремя педалями, шестерней, колесом и индикатором, проверяя зеркало. Но через месяц вы не можете вспомнить, почему вы нашли это настолько сложным. Добро пожаловать в радости программирования. Надеюсь, вы найдете это так же весело, как и я.
Sub ExtractMaxTemperatures()
' I understand your temperatures are in columns 2 to 5. If I use these values
' in the code and they change (perhaps because new columns are added) then you
' will have to search the code for the appropriate 2s and 5s and replace them.
' Constants allow me to use names which makes the code easier to understand.
' Also if the column numbers change, change the constants and the code is fixed.
' I have used Sheet1 to hold the full set of temperatures and Sheet2 to hold the
' extracted maximums. In these constants, and in the variables below, replacing
' "Sht1" and "Sht2" with something more meaningful will help make the code more
' readable.
Const ColSht1TempFirst As Long = 2
Const ColSht1TempLast As Long = 5
Const RowSht1DataFirst As Long = 3
Const ColSht2Dest As Long = 2
Const RowSht2Dest As Long = 3
Dim ColSht1Crnt As Long
Dim RowSht1Crnt As Long
Dim ColSht2Crnt As Long
Dim RowSht2Crnt As Long
' Declare fixed size arrays to hold the maximum temperature
' and its row for each column
Dim TempMaxByCol(ColSht1TempFirst To ColSht1TempLast) As Single
Dim RowForMaxTemp(ColSht1TempFirst To ColSht1TempLast) As Long
Dim TempCrnt As Single
Dim TempMaxCrnt As Single
Dim RowForMaxCrnt As Long
Dim ShtValue As Variant
' It is possible to check the values within the worksheet with statements
' such as "If .Cells(RowCrnt, ColCrnt).Value = 5 Then"
' However, it is much quicker to copy all values from the worksheet to an
' array and process the values from the array. I have done this since I
' will have to use arrays within the column loop.
' I do not know the name of the worksheet containing the temperatue so I have
' used Sheet1.
' The statement "ShtValues = .UsedRange.Value" converts ShtValues to a two
' dimensional array containing every value in in the worksheet. The rows
' are dimension 1 and the columns are dimension 2 which is not the usual
' arrangement. However, it means "ShtValue(RowCrnt, ColCrnt)" matches
' ".Cells(RowCrnt, ColCrnt).Value" which avoids confusion.
' Because I have loaded the entire worksheet, row and column numbers within
' the array will match those in the worksheet.
With Worksheets("Sheet1")
ShtValue = .UsedRange.Value
End With
' Loop for each temperature column
For ColSht1Crnt = ColSht1TempFirst To ColSht1TempLast
' Your code assume no blank or non-numeric values within the temperature
' ranges. However, were they to exist, the code would fail so I check.
RowForMaxCrnt = 0 ' Indicates no temperature saved yet
' Loop for each data row column. UBound(ShtValue, 2) identifies the last row.
For RowSht1Crnt = RowSht1DataFirst To UBound(ShtValue, 1)
If IsNumeric(ShtValue(RowSht1Crnt, ColSht1Crnt)) Then
' This cell is numeric
TempCrnt = Val(ShtValue(RowSht1Crnt, ColSht1Crnt))
If RowForMaxCrnt <> 0 Then
' A possible maximum temperature has already been stored.
' Check current value against it.
If TempMaxCrnt < TempCrnt Then
' Higher temperature found. Store details in temporary variables
RowForMaxCrnt = RowSht1Crnt
TempMaxCrnt = TempCrnt
End If
Else
' First temperature found. Store details in temporary variables
RowForMaxCrnt = RowSht1Crnt
TempMaxCrnt = TempCrnt
End If
End If
Next
'Store values in temporary variable in arrays
TempMaxByCol(ColSht1Crnt) = TempMaxCrnt
RowForMaxTemp(ColSht1Crnt) = RowForMaxCrnt
Next
' Initialise the current row to the start row of the outout table
RowSht2Crnt = RowSht2Dest
' I think you call the destination sheet "Calc" but I have used "Sheet2"
With Worksheets("Sheet2")
' Create header lines
' TC1 TC2 TC3 TC4
' Max Row Max Row Max Row Max Row
' This code will handle multiple header rows
For RowSht1Crnt = 1 To RowSht1DataFirst - 1
ColSht2Crnt = ColSht2Dest
For ColSht1Crnt = ColSht1TempFirst To ColSht1TempLast
' Merge two cells together ready for column name
.Range(.Cells(RowSht2Crnt, ColSht2Crnt), _
.Cells(RowSht2Crnt, ColSht2Crnt + 1)).Merge
With .Cells(RowSht2Crnt, ColSht2Crnt)
.Value = ShtValue(RowSht1Crnt, ColSht1Crnt)
.HorizontalAlignment = xlCenter
End With
ColSht2Crnt = ColSht2Crnt + 2
Next
RowSht2Crnt = RowSht2Crnt + 1
Next
' Now add "Max Row Max Row Max Row Max Row" row
ColSht2Crnt = ColSht2Dest
For ColSht1Crnt = ColSht1TempFirst To ColSht1TempLast
With .Cells(RowSht2Crnt, ColSht2Crnt)
.Value = "Max"
.HorizontalAlignment = xlRight
End With
ColSht2Crnt = ColSht2Crnt + 1
With .Cells(RowSht2Crnt, ColSht2Crnt)
.Value = "Row"
.HorizontalAlignment = xlRight
End With
ColSht2Crnt = ColSht2Crnt + 1
Next
RowSht2Crnt = RowSht2Crnt + 1
' Now create data row
ColSht2Crnt = ColSht2Dest
For ColSht1Crnt = ColSht1TempFirst To ColSht1TempLast
.Cells(RowSht2Crnt, ColSht2Crnt).Value = TempMaxByCol(ColSht1Crnt)
ColSht2Crnt = ColSht2Crnt + 1
.Cells(RowSht2Crnt, ColSht2Crnt).Value = RowForMaxTemp(ColSht1Crnt)
ColSht2Crnt = ColSht2Crnt + 1
Next
End With
End Sub
Редактировать Добавление потому что ОП хочет, чтобы выбрать столбцы, из которых максимальные значения должны быть выбраны.
Если я правильно понял ваш комментарий правильно:
- Число строк устанавливается на 30.
- Вы хотите, чтобы выбрать столбцы, из которых максимумы должны быть извлечены во время выполнения.
Приведенный выше код обрабатывает любое количество строк. Я предлагаю вам оставить это без изменений, даже если вы считаете, что номер будет всегда 30.В течение моей карьеры я слышал, что «это требование никогда не может меняться» много раз, чтобы услышать год или два позже «Извините, это изменилось».
Был один аспект вышеприведенного кода, который, как я думал, был слабым, но который я не хотел исправлять, потому что он бы добавил осложнения, которые не хотели объяснять. Я использовал .UsedRange
для загрузки данных с рабочего листа. Это самый простой способ, но определение .UsedRange
не всегда соответствует ожидаемому. .UsedRange
включает строки и столбцы, которые были отформатированы (например, изменена высота или ширина), но в противном случае они не используются. В этом answer of mine к более раннему вопросу я включаю макрос, который демонстрирует ряд методов для поиска окончательной строки или столбца и показывает, где каждый метод терпит неудачу. Я не думаю, что это важно для вашего текущего вопроса, но я предлагаю вам сохранить этот макрос и поэкспериментировать с ним позже.
Рассмотрим этот макрос:
Sub TestGetRange()
Dim CalcRange As Range
Dim Reply As Long
Do While True
Err.Clear
On Error Resume Next
Set CalcRange = Application.InputBox(Prompt:="Select columns to be copied", _
Title:="Extract maximum temperatures", Type:=8)
On Error GoTo 0
If Err.Number <> 0 Then
Reply = MsgBox(Prompt:="Do you wish to exit without extracting any temperatures?", _
Buttons:=vbYesNo, Title:="Extract maximum temperatures")
If Reply = vbYes Then
' User wants to exit
Exit Do
End If
' Loop for another go
Else
' User had entered a valid range
Exit Do
End If
Loop
If CalcRange Is Nothing Then
Debug.Print "User wants immediate exit"
Exit Sub
Else
Debug.Print CalcRange.Address
End If
End Sub
Как я уже говорил ранее, если пользователь нажмет Отменить, есть синтаксическая ошибка времени выполнения, и пользователь должен выбрать Debug и нажмите F5 к Продолжать. Это тип ситуации, для которой подходит On Error Resume Next
. Я добавил это к вашему исходному коду и включил опцию выхода. Этот макрос не использует введенный диапазон, кроме отображения его адреса.
Использование Ctrl + Left Mouse вы можете выбрать несмежные диапазоны. Вы не говорите, хотите ли вы выбирать столбцы 4, 5, 11 и 12, но поскольку вы не можете остановить выбор пользователем несмежных диапазонов, я включил код для их обработки.
Я использовал этот макрос несколько раз. В первый раз, когда я выбрал столбцы B и C, в следующий раз, когда я отменил, я выбрал различные смешанные диапазоны. Выход был:
$B:$C
User wants immediate exit
$B$1,$D$1
$B$1,$C$1,$E$1
$B$1:$D$1
$B:$B,$E:$E
$B:$C,$E:$E,$F:$F,$H:$H
$B:$B,$E$2
Обратите внимание, что я получаю $B:$B
или $B:$C
, если выбрать столбцы и $E$1
, если выбрать ячейку. В последней строке я выбрал и столбец, и ячейку.
Имейте игру с этим макросом и получите представление о диапазонах, которые он может получить от пользователя.
Как-то вам нужно преобразовать диапазон, полученный от пользователя, в один или несколько столбцов.
Добавьте этот код в нижней части выше макроса:
Dim Count As Long
Dim RngCrnt As Range
Count = 0
For Each RngCrnt In CalcRange
Debug.Print " " & RngCrnt.Address
Count = Count + 1
If Count = 10 Then
Exit For
End If
Next
Debug.Print CalcRange.EntireColumn.Address
For Each RngCrnt In CalcRange.EntireColumn
Debug.Print " " & RngCrnt.Address
Count = Count + 1
If Count = 10 Then
Exit For
End If
Next
В этом коде я использовал For Each
заявление, чтобы разделить диапазон от пользователя в поддиапазонах. Я управлял макрокоманду, выбранные столбцы В и С и получили следующий результат:
$B:$C
$B$1
$C$1
$B$2
$C$2
$B$3
$C$3
$B$4
$C$4
$B$5
$C$5
$B:$C
$B:$B
$C:$C
С первым For Next
, суб-диапазон представляет собой клетку. Если бы я пропустил код, ограничивающий вывод до 10, я бы получил одну строку отображения на ячейку в каждом столбце.
Во втором For Next
, я скорректировал диапазон пользователей, добавив .EntireColumn
. Это не влияет на адрес, отображаемый Debug.Print CalcRange.EntireColumn.Address
, но изменил поддиапазон на столбец, который я хочу.
Я думаю, что это новая информация, необходимая для понимания пересмотренного макроса. Я надеялся дать вам список изменений, но слишком много изменений, чтобы сделать это практичным.
Sub ExtractMaxTemperatures2()
' Adjusted to handle user selected columns
Const RowSht1DataFirst As Long = 2 ' First non-header row in Sheet1
Const ColSht2Dest As Long = 2 ' Left column \ of table of extracted
Const RowSht2Dest As Long = 3 ' Top row / values in Sheet2
Dim ColLogicalCrnt As Long ' 1, 2, 3 and so on regardless of true column number
Dim ColSht1Crnt As Long ' Current column within Sheet1
Dim ColSht2Crnt As Long ' Current column within Sheet2
Dim NumColsSelected As Long ' Number of columns selected.
Dim Reply As Long ' Return value from InputBox
Dim RowForMaxCrnt As Long ' Row holding maximum temperature found so far within current column
Dim RowSht1Crnt As Long ' Current row within Sheet1
Dim RowSht2Crnt As Long ' Current row within Sheet2
Dim RngColCrnt As Range ' Sub-range of user selected range giving current column
Dim RngUserSelected ' Range selected by user then adjusted with .EntireColumn
Dim ShtValue As Variant ' 2D array holding values loaded from Sheet1
Dim TempCrnt As Single ' The temperature from the current cell
Dim TempMaxCrnt As Single ' Maximum temperature found so far within current column
' Declare arrays to hold the maximum temperature and its row for each column.
' These arrays will be sized at runtime.
Dim TempMaxByCol() As Single ' Maximum temperature for each logical column
Dim RowForMaxTemp() As Long ' Row for maximum temperature for each logical column
With Worksheets("Sheet1")
ShtValue = .UsedRange.Value
.Activate ' Necessary to ensure Sheet1 visible for range selection
End With
Do While True
Err.Clear
On Error Resume Next
Set RngUserSelected = Application.InputBox(Prompt:="Select columns to be copied", _
Title:="Extract maximum temperatures", Type:=8)
On Error GoTo 0
If Err.Number <> 0 Then
Reply = MsgBox(Prompt:="Do you wish to exit without extracting any temperatures?", _
Buttons:=vbYesNo, Title:="Extract maximum temperatures")
If Reply = vbYes Then
' User wants to exit
Exit Do
End If
' Loop for another go
Else
' User had entered a valid range
Exit Do
End If
Loop
If RngUserSelected Is Nothing Then
Debug.Print "User wants immediate exit"
End If
' Convert any cells to columns
Set RngUserSelected = RngUserSelected.EntireColumn
' Count number of selected columns
NumColsSelected = 0
For Each RngColCrnt In RngUserSelected
NumColsSelected = NumColsSelected + 1
Next
' Size arrays for number of selected columns
ReDim TempMaxByCol(1 To NumColsSelected) As Single
ReDim RowForMaxTemp(1 To NumColsSelected) As Long
' Fill TempMaxByCol and RowForMaxTemp with extracted values
ColLogicalCrnt = 0
' Loop for each temperature column
For Each RngColCrnt In RngUserSelected
ColSht1Crnt = RngColCrnt.Column
ColLogicalCrnt = ColLogicalCrnt + 1 ' Logical column for this physical column
RowForMaxCrnt = 0 ' Indicates no temperature saved yet
' Loop for each data row column. UBound(ShtValue, 2) identifies the last row.
For RowSht1Crnt = RowSht1DataFirst To UBound(ShtValue, 1)
If IsNumeric(ShtValue(RowSht1Crnt, ColSht1Crnt)) Then
' This cell is numeric
TempCrnt = Val(ShtValue(RowSht1Crnt, ColSht1Crnt))
If RowForMaxCrnt <> 0 Then
' A possible maximum temperature has already been stored.
' Check current value against it.
If TempMaxCrnt < TempCrnt Then
' Higher temperature found. Store details in temporary variables
RowForMaxCrnt = RowSht1Crnt
TempMaxCrnt = TempCrnt
End If
Else
' First temperature found. Store details in temporary variables
RowForMaxCrnt = RowSht1Crnt
TempMaxCrnt = TempCrnt
End If
End If
Next
'Move values from temporary variables to arrays
TempMaxByCol(ColLogicalCrnt) = TempMaxCrnt
RowForMaxTemp(ColLogicalCrnt) = RowForMaxCrnt
Next
' Initialise the current row to the start row of the outout table
RowSht2Crnt = RowSht2Dest
' I think you call the destination sheet "Calc" but I have used "Sheet2"
With Worksheets("Sheet2")
' Create header lines
' TC1 TC2 TC3 TC4
' Max Row Max Row Max Row Max Row
' This code will handle multiple header rows
For RowSht1Crnt = 1 To RowSht1DataFirst - 1
ColSht2Crnt = ColSht2Dest
ColLogicalCrnt = 0
For Each RngColCrnt In RngUserSelected
ColSht1Crnt = RngColCrnt.Column
ColLogicalCrnt = ColLogicalCrnt + 1 ' Logical column for this physical column
' Merge two cells together ready for column name
.Range(.Cells(RowSht2Crnt, ColSht2Crnt), _
.Cells(RowSht2Crnt, ColSht2Crnt + 1)).Merge
With .Cells(RowSht2Crnt, ColSht2Crnt)
.Value = ShtValue(RowSht1Crnt, ColSht1Crnt)
.HorizontalAlignment = xlCenter
End With
ColSht2Crnt = ColSht2Crnt + 2
Next
RowSht2Crnt = RowSht2Crnt + 1
Next
' Now add "Max Row Max Row Max Row Max Row" row
ColSht2Crnt = ColSht2Dest
' ColLogicalCrnt = 0 ' Don't need logical column for this loop
For Each RngColCrnt In RngUserSelected
ColSht1Crnt = RngColCrnt.Column
With .Cells(RowSht2Crnt, ColSht2Crnt)
.Value = "Max"
.HorizontalAlignment = xlRight
End With
ColSht2Crnt = ColSht2Crnt + 1
With .Cells(RowSht2Crnt, ColSht2Crnt)
.Value = "Row"
.HorizontalAlignment = xlRight
End With
ColSht2Crnt = ColSht2Crnt + 1
Next
RowSht2Crnt = RowSht2Crnt + 1
' Now create data row
ColSht2Crnt = ColSht2Dest
ColLogicalCrnt = 0
' Loop for each temperature column
For Each RngColCrnt In RngUserSelected
' ColSht1Crnt = RngColCrnt.Column ' Don't need Sheet 1 column for this loop
ColLogicalCrnt = ColLogicalCrnt + 1 ' Logical column for this physical column
.Cells(RowSht2Crnt, ColSht2Crnt).Value = TempMaxByCol(ColLogicalCrnt)
ColSht2Crnt = ColSht2Crnt + 1
.Cells(RowSht2Crnt, ColSht2Crnt).Value = RowForMaxTemp(ColLogicalCrnt)
ColSht2Crnt = ColSht2Crnt + 1
Next
End With
End Sub
Большое спасибо Тони !, это было очень поучительно. Я рад, что вы помогли мне не только ответить на этот вопрос, но и научить меня. Читая ответ, я вижу, что у меня долгий длинный путь ... Извиниться за то, что я не выражаю себя ясно, английский не мой родной язык, и мои знания основаны на vb. – Jero
Что касается кода, вы получили почти все, что я хотел сделать, но одна из моих проблем заключается в том, что я никогда не использую такое же количество столбцов. Строковые данные всегда содержат одинаковое количество из них (30), но я не хочу показывать их все в «sheet2», только те, которые мне нужны иногда 10 других 12 и т. Д. Вот почему я хотел выбрать вручную до. Но даже не знаю, можно ли это сделать. Cheers – Jero
Я забыл маленький неанглийский, которого я когда-либо знал, поэтому не могу никого критиковать. Ваш английский был в порядке. Трудно точно объяснить, что вы хотите, даже на своем родном языке. –